整理笔记--Java基础

JAVA学习网 2018-03-15 00:24:03

这个系列如标题,只是个人笔记的记录,会不断修改的,争取越记越少。

----------------------------------------------------------------------------------------------------

常用的Dos命令:

  d:盘符切换

  dir:列出当前目录下的文件和文件夹

  md:创建目录

  rd:删除目录

  cd:进入指定目录

  del:删除文件

  cls:清屏

  exit:退出命令行

 

进制:

  二进制:由0,1组成。以0b开头。

  八进制:由0-7组成。以0开头。

  十进制:由0-9组成。整数默认是十进制。

  十六进制:有0-9,a,b,c,d,e,f(大小写均可)。以0x开头。

  相互之间的转换-->用计算器去

 

数据类型分为:

  1.基本数据类型 

  2.引用数据类型(class,interface,array)

基本数据类型,从小到大:

byte,char,short-->int(整数默认)-->float(要加f或F)-->long(要加l或L)--double(浮点数默认)。

 强制转换-->不建议-->会损失精度。

 

&和&&的区别:

  前者,左边条件无论真假,右边条件都进行运算;

  后者,左边条件为真,右边条件才参与运算。

  

位运算:

 

流程控制语句:

if else | do while(最少执行一次) | while | for |

switch(变量){

  case 值:要执行的语句;

  break; //没有它会出现case穿透

  default:要执行的语句 //上面case都不是时才执行的语句

}

补充:continue(继续) return(返回)

 

 转义字符:

\n-->换行  \r-->回车  \t-->跳到下一个tab位置

 

封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

权限修饰符:

  

 

this和super的区别:

  1.this代表的是本类对象的引用 ,可以调用本类中的成员和构造方法,也可以调用父类中的成员(当本类中没有的时候)。

  2.super代表的是父类空间的引用,只能调用父类中的成员和构造方法。

注意事项:

this和super只能在构造方法的第一个语句中调用,且不能同时存在。

 

final:

  1.修饰类,类不可被继承。 

  2.修饰方法,方法不可被重写。 

  3.修饰变量,变量变为常量,只能赋值一次。

final修饰局部变量:

  1.如果局部变量是基本类型,它的值是不能变的。

  2.如果局部变量是引用类型,它的地址值不能变,但是该对象中的内容是可以变化的。

final修饰变量的初始化时机:

  1.在定义常量的时候直接显示初始化,如:final int x = 10。

  2.在对象创建完毕之前完成初始化(在构造方法里面和构造代码块里)。

注意事项:

  1.上面两张初始化方式不能同时进行。

  2.当final修饰成员变量时,系统默认的初始化是无效的。

假设系统默认的初始化值是有效的,那么就代表引用数据类型默认的初始化值是null。但是被final修饰的变量,只能被赋值一次。那么,在使用该引用数据类型的变量时,只能使用null了,这样没有意义。

 

局部变量和成员变量的区别:

  1.成员变量直接定义在类中,在类中有效。

  局部变量定义在方法中,参数上,语句中。在所属大括号有效。

  2.成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。

  局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。

  3.成员变量有默认的初始化值,其中引用数据的初始化值为null。

  局部变量没有默认的初始化值,使用前必须赋值。

 

静态变量和成员变量的区别:

  1.成员变量所属于对象,所以也称为实例变量。

  静态变量所属于类,所以也称为类变量。

  2.成员变量存在于堆内存中。

  静态变量存在于方法区中。

  3.成员变量随着对象创建而存在,随着对象被回收而消失。

  静态变量随着类的加载而存在,随着类的消失而消失。

  4.成员变量只能被对象所调用 。

  静态变量可以被对象调用,也可以被类名调用。

所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

 

static:

  1.修饰成员变量和成员方法 

  2.静态代码块 

  3.静态内部类

  4.静态导入

import static java.lang.Math. *静态导包

静态导包不能导Arrays.toString方法,因为这样系统及不知道你到底用的是哪个toString方法。

总结:

  1.随类被共享,可用类名调用,优先于对象存在。

  2.静态只能访问静态。

  3.静态方法中不能使用this或super关键字。

  4.如果一个类中的所有方法都是静态的 ,那么就要私有化其构造方法,防止其他类创建本类对象。

 

构造代码块和构造函数有什么区别?

  1.构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块。只要对象一建立。就会调用这个代码块。

  2.构造函数:是给与之对应的对象进行初始化。它具有针对性。

构造函数和一般函数有什么区别呢? 

  1.两个函数定义格式不同。

  2.构造函数是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次。一般函数,是对象创建后,需要调用才执行,可以被调用多次。

注意:

一个类在定义时,如果没有定义过构造函数,那么该类中会自动生成一个空参数的构造函数,为了方便该类创建对象,完成初始化。如果在类中自定义了构造函数,那么默认的构造函数就没有了。

 

{代码块}分类:

  A.局部代码块:在方法中出现,限定变量生命周期,及早释放,提高内存利用率。

  B.构造代码块(初始化块):在类中方法外出现,多个构造方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行。

  C.静态代码块:在类中方法外出现,并加上static修饰,用于给类进行初始化,在加载的时候就执行,并且只执行一次,一般用于加载驱动。

  D.同步代码块:被synchronized 修饰的代码块。

执行顺序:

主方法所在的类的静态代码块-->主方法-->构造代码块-->构造方法。

 

继承:

  1.子类只能继承父类所有非私有的成员(成员方法和成员变量) 。其实这也体现了继承的另一个弊端:打破了封装性。

  2.子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。

  3.继承中的成员变量及方法关系-->就近原则。

在继承中构造方法的特点:

子类中的所有构造方法都会默认访问父类中的空参构造方法:super()。

如果在继承时父类中没有空参构造方法怎么办?

使用super()指定访问父类中的有参构造,把具体的参数传递给父类。

 

重载和重写的区别:

方法重载:

  1.在同一个类中。 

  2.方法名相同,参数列表不同,与返回值类型无关。

方法重写:

  1.有继承关系。   

  2.子父类中有相同的方法。

补充:Overloaded(重载)的方法是可以改变返回值的类型。

 

多态:事物的多种形态。

  1.要有继承或者实现关系。 

  2.要有方法重写。 

  3.要有父类引用指向子类对象。

多态中的成员访问特点:

  1.成员变量:编译看左边,运行看左边。

  2.构造方法:子类的构造都会默认访问父类构造成员方法。 

  3.成员方法:编译看左边,运行看右边。

静态方法:

编译看左边,运行看左边。所以静态方法不能算方法的重写。

好处:

  1.提高了代码的维护性(继承保证)。

  2.提高了代码的扩展性(由多态保证)。

弊端:

不能使用子类特有的成员变量和成员方法。

只能通过转型才能使用子类的特有功能:

  1.向上转型:从子到父,父类引用指向子类对象 。

如:Fu f = new Zi();

  2.向下转型:从父到子,父类引用转为子类对象 

如:Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。

这时候,可以通过instanceof关键字来判断前边的引用是否是后边的数据类型接口。

if (a instanceof Cat) {}

 

内部类:把类定义在其他类的内部,这个类就被称为内部类。 

内部的访问特点:

  1.内部类可以直接访问外部类的成员,包括私有。 

  2.外部类要访问内部类的成员,必须创建对象。

分类:

  1.成员内部类

  2.局部内部类

  3.静态内部类

  4.匿名内部类

 

抽象类和接口:

abstract不能和哪些关键字共用?

  1.abstract和static:

  被abstract修饰的方法没有方法体。被static修饰的可以用类名.调用,但是类名.调用抽象方法是没有意义的。

  2.abstract和final:

  被abstract修饰的方法强制子类重写。被final修饰的不让子类重写,所以他俩是矛盾。

  3.abstract和private:

  被abstract修饰的是为了让子类看到并强制重写。被private修饰不让子类访问,所以他俩是矛盾的。

两者的区别(jdk1.7前):

  a.抽象类:

  (1)有常量也有变量

  (2)有抽象方法也有非抽象方法 

  (3)只能被单继承 

  (4)成员修饰符可以自定义

  (5)有构造方法

  (6)能包含初始化块

  (7)可以不重写所有的抽象方法,但是类必须声明称抽象的

  b.接口: 

  (1)只有全局常量

public static final int num=3;

  (2)只有抽象方法

public abstract void show();

  (3)可以被多实现 

  (4)成员修饰符只能是public       

  (5)没有构造方法 

  (6)不能包含初始化块

  (7)必须重写所有的抽象方法

两者的相同点:都不能实例化。

 

异常:

  

try {  
    // 可能会发生异常的程序代码  
} catch (Type1 id1) {  
    // 捕获并处理try抛出的异常类型Type1  
} catch (Type2 id2) {  
    // 捕获并处理try抛出的异常类型Type2  
} finally {  
    // 无论是否发生异常,都将执行的语句块  
}

补充:throws和throw 

自定义异常(略)

 

final finally finalize 三个有什么区别?

  1.final 可以修饰的是类,方法,变量。

  2.finally可以修饰的是代码块,与try catch一起使用。

  3.finalize它是一个方法名,在Object类中定义的,在对象被回收的时候调用的。finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

finalize()方法什么时候被调用?

在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。一般建议在该方法中释放对象持有的资源。

finally是在return前还是return后执行的?

return 语句先执行,但是并没有彻底返回,,先去检查有没有finally语句,如果有先把它执行完,然后再彻底返回,finally中的内容是不会改变return的结果的。

注意事项:

finally里面不要写return语句!

 

java.lang.Object:

  1.boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。
  而根据对象的属性不同,判断对象是否相同的具体内容也不一样。所以在定义类时,一般都会复写equals方法,建立本类特有的判断对象是否相同的依据。
  2.String toString():将对象变成字符串。
  默认返回的格式:类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())。为了对象对应的字符串内容有意义,可以通过复写,建立该类对象自己特有的字符串表现形式。
  3.Class getClass():获取任意对象运行时的所属字节码文件对象。
  4.int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表的性能。
  通常equals,toString,hashCode,在应用中都会被复写,建立具体对象的特有的内容。

 

java.lang.System:属性和行为都是静态的。

  long currentTimeMillis(); // 返回当前时间毫秒值。
  exit(); // 退出虚拟机。
  Properties getProperties() ;  // 获取当前系统的属性信息。
  Properties prop = System.getProperties(); //获取系统的属性信息,并将这些信息存储到Properties集合中。
  System.setProperty("myname","老师"); //给系统属性信息集添加具体的属性信息。
  //临时设置方式:运行jvm时,可以通过jvm的参数进行系统属性的临时设置,可以在java命令的后面加入 –D=  用法:java –Dmyname=小明 类名。
  String name = System.getProperty("os.name");//获取指定属性的信息。

 

日期包(略):

SimpleDateFormat是不是线程安全的?

SimpleDateFormat不是线程安全的。用户应为每个线程创建一个单独的实例。格式化同一个时间,如果格式化出来的结果不一致的或者抛出了异常,就证明SimpleDateFormat确实是线程不安全的。

例子:

public class SimpleDateFormatTest {
 
    public static void main(String[] args) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateTime = "2016-12-30 15:35:34";
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            System.out.println(
                                    Thread.currentThread().getName() + "\t" + dateFormat.parse(dateTime));
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
}

 

结论:

可以发现,不光是抛出了异常,而且打印出来的结果存在不一致的现象。看来,SimpleDateFormat果然是线程不安全的。

在SimpleDateFormat的父类DateFormat中可以看到一个成员变量,注释说,日历是用于格式和解析时用的。

另外,因为日历作为一个成员变量,在多线程场景下,会发生资源共享造成前后不一致的问题。这就是SimpleDateFormat线程不安全的原因。

如何避免:

使用ThreadLocal来存放SimpleDateFormat。ThreadLocal的特性决定了每个线程操作ThreadLocal中的值,不会影响到别的线程。

ThreadLocal是一个关于创建线程局部变量的类。

通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

 

字符串:

统计大串中子串出现的次数?

String big="good good study,day day up";
        String small="day";
        int count=0;
        while(big.indexOf(small)!=-1){
        big=big.substring(big.indexOf(small)+small.length());
        count++;
        System.out.println(count);
        }

例子1:

/*分析以下需求,并用代码实现:
(1)利用键盘录入,输入一个字符串
(2)统计该字符串中各个字符的数量
(3)如:
  用户输入字符串"If~you-want~to~change-your_fate_I_think~you~must~come-to-the-dark-horse-to-learn-java"
  程序输出结果:-(9)I(2)_(3)a(7)c(2)d(1)e(6)f(2)g(1)h(4)i(1)j(1)k(2)l(1)m(2)n(4)o(8)r(4)s(2)t(8)u(4)v(1)w(1)y(3)~(6)
*/
public class Tong_1 {
  public static void main(String[] args) {
         String s="If~you-want~to~change-your_fate_I"
                       + "_think~you~must~come-to-the-dark-horse-to-learn-java";
         char[] arr = s.toCharArray();
         TreeMap<Character,Integer>tm=new TreeMap<>();
         //HashMap和TreeMap的排序不同
         //HashMap<Character,Integer>tm=new HashMap<>();
         for (char c : arr) {
                if(!tm.containsKey(c)){
                       tm.put(c, 1);
                }else{
                       tm.put(c, tm.get(c)+1);
                }
         }
         for (char key : tm.keySet()) {
                System.out.println(key+"("+tm.get(key)+")");
         }
  }
}

例子2:

/*分析以下需求,并用代码实现:
(1)统计每个单词出现的次数
(2)有如下字符串"If you want to change your fate I think you must come to the dark horse to learn java"(用空格间隔)
(3)打印格式:
  to=3
         think=1
         you=2*/
public class Tong_2 {
  public static void main(String[] args) {
         String s="If you want to change your fate I think you "
                       + "must come to the dark horse to learn java";
         String[] arr = s.split(" ");
         HashMap<String,Integer>hm=new HashMap<>();
         for (int i = 0; i < arr.length; i++) {
                if(!hm.containsKey(arr[i])){
                       hm.put(arr[i],1);
                }else{
                       hm.put(arr[i], hm.get(arr[i])+1);
                }
         }
         for (String key : arr) {
                System.out.println(key+"="+hm.get(key));
         }
  }
}

StringBuffer 与StringBuilder以及String的区别:

  1.StringBuffer:从JDK1.0的时候出现的,是线程安全的,效率较低。

  StringBuilder::从JDK1.5出现的,线程不安全,效率比较高。

  2.StringBuffer和StringBuilderf都是一个可变的字符序列。

  String是一个不可变的字符序列。

补充:三者都实现了CharSequence接口。

 

正则表达式:

例子:

package com.demo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*需求:写一个正则, 把以下提供的内容中的所有邮箱都获取出来,输出到控制台:
在2008年有时候,我注册了我的第一个邮箱:xiaoxuesheng@163.com,后来,因为邮箱号码太好被盗了,所以,
我不得不又注册了人生中的第二个邮箱: gaozhongsheng@yahoo.com.cn,但是,作为一个优秀的程序员,只有一个邮箱是不够的, 
所以,在2013年的时候,再次注册了两个邮箱:zhuangbi@iphone.cn和99886128@qq.com,现在我又有了一个新的邮箱:niubi@itheima.com*/
public class Test01 {
        public static void main(String[] args) {
               String s="在2008年有时候,我注册了我的第一个邮箱:xiaoxuesheng@163.com,后来,因为邮箱号码太好被盗了"
                             + ",所以,我不得不又注册了人生中的第二个邮箱: "
                          + "gaozhongsheng@yahoo.com.cn,但是,作为一个优秀的程序员,"
                         + "只有一个邮箱是不够的, 所以,在2013年的时候,再次注册了两个邮箱:"
                          + "zhuangbi@iphone.cn和99886128@qq.com,现在我又有了一个新的邮箱:niubi@itheima.com";
           String regex="\\w+@\\w+(\\.\\w+)+";
           Pattern p=Pattern.compile(regex);
           Matcher m=p.matcher(s);
            while(m.find()){
                   System.out.println(m.group());
         }
    }
}

 

泛型:主要解决运行时异常,如:类型转换异常ClassCastException

  1.好处:提高安全性(将运行期的错误转换到编译期),省去强转的麻烦。

  2.前后的泛型必须一致,或者后面的泛型可以省略不写(即JDK1.7的新特性:菱形泛型)。

  3.泛型只在编译期存在,在运行期会被擦除,生成的类中是没有泛型的。

  4.(1)向上限定边界,谁继承E。

     使用场景:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。

     (2)向下限定边界,E的父类是谁。

     使用场景:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类。

  5.泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是?

   使用场景:当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用?通配符来表未知类型。

  6.泛型类 (当类中的操作的引用数据类型不确定的时候,可以使用)

class Tool<Q> {
    private Q obj;  
    //setter/getter    
}

  7.泛型方法(当方法操作的引用数据类型不确定的时候,可以使用)

public <W> void method(W w) {
         System.out.println("method:"+w);
  }

  8.泛型接口

interface Inter<T> {
  void show(T t);
}
class InterImpl<R> implements Inter<R> {
  public void show(R r) {
         System.out.println("show:"+r);
  }
}

种类:

E – Element (在集合中使用,因为集合中存放的是元素)
T – Type(Java 类)
K – Key(键)
V – Value(值)
N – Number(数值类型)

 

递归:自己调用自己

 

反射:动态加载一个指定的类,并获取该类中的所有的内容。。

  1.类的常见加载时机

  将字节码文件从硬盘加载到内存中,并生成一个Class对象

  2.类加载器

  1).Bootstrap Classloader:根类加载器

  加载的是核心类库的内容,rt.jar

  2).Extension Classloader:扩展类加载器

  加载的是JDK中lib目录下的ext目录中的jar包

  3).System Classloader:系统类加载器

  加载的是我们写的类

好处:极大的增强了程序的扩展性。

 

基本步骤: 

  1、获得Class对象,就是获取到指定的名称的字节码文件对象。

  2、实例化对象,获得类的属性、方法或构造函数。

  3、访问属性、调用方法、调用构造函数创建对象。

 

获取字节码对象的方式:

好处:不用修改源码,直接修改配置文件即可,降低耦合性。

1.通过反射获取构造方法的对象:

//构造方法的类型:Constructor
//获取公有构造方法的对象方式:
Class clazz = Class.forName(className);
Constructor c = clazz.getConstructor(String.class);
//获取所有权限的构造方法的对象方式:
Constructor c = clazz.getDeclaredConstructor(String.class);
//去除私有权限
c.setAccessible(true);
Person p = (Person)c.newInstance("乔峰");

 

2.通过反射获取成员变量的对象:

//成员变量的类型:Field
//获取一个公有的成员变量对象:
Field f = clazz.getField(字段名);
//获取一个所有权限的成员变量对象:
Field f = clazz.getDeclaredField(字段名);
//去除私有权限
f.setAccessible(true);
//给该字段设置值
f.set(对象,值);
//获取该字段的值
Object value = f.get(对象);

 

3.通过反射获取成员方法的对象:

//成员方法对象的类型: Method
//获取一个公有的成员方法对象:
Method m = clazz.getMethod(方法名,String.class);
//获取一个所有权限的成员方法对象:
Method m = clazz.getDeclaredMethod(方法名,String.class);
//去除私有权限
m.setAccessible(true);
//调用成员方法对象:
m.invoke(对象,"乔峰");

注意事项:

直接使用暴力反射,方便迅捷。

 

枚举:

 

阅读(734) 评论(0)