Java复习1

java基础类型

  • 整形:int,long,short,byte
  • 浮点型:double,float
  • 布尔类型:boolean
  • 字符类型:char

int 4个字节
long 8个字节
short 2个字节
byte 1个字节

double 8个字节
float 4个字节

boolean 1个bit, 八分之一个字节

char 2个字节

引用类型

  • 强引用
    • 当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
  • 软引用
    • 如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
    • 可以用来内存敏感的高速缓存
    • 可以配合referenceQueue一起使用,当引用被回收的时候,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。
  • 弱引用
    • 与软引用类似,弱引用与软引用的区别在于:更短的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
    • 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中
  • 虚引用
    • 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

包装类型:

  • 由于java有基础类型,基础类型本身并不是对象,所以其实本身违反了java的面向对象思想,所以将基础数据类型转换对应的包装类型,那么就可以操作基础类型像操作对象一样了。
基础类型 包装类型
boolean Boolean
char Character
long Long
byte Byte
int Integer
short Short
float Float
double Double
  • 自动拆箱和装箱
    • 拆箱:
      • 自动将包装类型转换成基础数据类型
      • 自动拆箱如果遇到包装给null,则会抛出NEP
    • 装箱:把基础数据类型转换成包装数据类型
      • 装修的数据如果是-128-127之间的数据。调用integer.valueof()会被缓存起来,具体源码如下,
        那么此时访问integer对象使用== , 还是equals的结果是一致的,都是常量池的数据比较。访问在此区间之外的数据则是内存比较
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Object

  • object具体方法:

    • hashcode()
    • equals()
    • wait()
    • notify()
    • notifyall()
    • clone()
    • toString()
    • getClass()
    • finalize() //实例被垃圾回收器回收的时候触发的操作
    • registerNatives()
  • equals 和 ==

    • 如果是两个引用类型比较,那么就是内存地址比较
    • == 不能重写值比较
    • 所有对象集成object都有equals方法,默认实现是==
  • equals 和 hashcode

    • 当equals方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规约定:值相同的对象必须有相同的hashCode。
    • hashCode不同时,object1.equals(object2)为false;
    • hashCode相同时,object1.equals(object2)不一定为true;
    • 当我们向一个Hash结构的集合中添加某个元素,集合会首先调用hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则链到后面(如果是链地址法)。
  • string sringbuffer stringbuilder

    • 三个都是final 类,不可以被集成
    • string不可变,sringbuffer stringbuilder长度可变
    • sringbuffer是线程安全的
    • stringbuilder线程不安全
    • StringBuffer在StringBuilder的方法之上添加了synchronized,保证线程安全。
  • string是final类,不可以继承,也不可以重写java.lang.String(类加载机制)

    • 双亲委派模型
      • 因为加载某个类的时候,优先使用父类加载器来加载需要使用的类。如果我们自定义了
        java.lang.String这个类
      • 加载该类的类加载器为appclassloader
      • appclassloader的父加载器为extclaassloader,所以这是加载string的类为extclassloader 但是在jre/lib/ext 下并没有发现string.class信息
      • 所以接着上传给顶级加载器bootstrap。
      • bootstrap在jre/lib目录下的rt.jar找到了string.class, 将其加载到内存中。
      • 这就会类加载器的委托模型
      • 所以用户自定义的类加载器不会生效
  • substring

    • 会创建一个新的string
    • 编译时会吧+转换成stringbuilder 的append方法
    • String = “abc”, 可能创建一个或者不创建对象,如果“ABC”在常量池中不存在,那么久会在常量池中创建一个String 对象,然后将对象的指向这个内存地址,以后无论创建多少次“abc”,都是相同的内存地址,
    • 注意: substring和new String 都是在堆中重新创建一个对象。
  • 常量池

    • String str = new String(“ABC”); 至少创建一个对象,也可能两个。因为用到new关键字,肯定会在heap中创建一个str2的String对象,它的value是“ABC”。同时如果这个字符串在字符串常量池里不存在,会在池里创建这个String对象“ABC”。

      • String s1= “a”;
      • String s2 = “a”;
      • 此时s1 == s2 返回true
      • “” 引号创建的对象在常量池塚
      • String s1= new String(“a”);
      • String s2 = new String(“a”);
      • 此时s1 == s2 返回false
      • 如果“a”在常量池中,那么仅在堆中拷贝一份引用(new String)
      • 如果“a”不在常量池塚,那么会现在常量池中创建一份数据,然后在进行堆拷贝,
      • 会创建两个对象
  • 编译优化
    final 都会在编译器优化,并且会被直接算好

  • String intern

    • jdk7以后吧常量池从方法区移到了堆上
    • 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。

String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);// false

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);// true

String s = newString("1"),生成了常量池中的“1” 和堆空间中的字符串对象。
s.intern(),这一行的作用是s对象去常量池中寻找后发现"1"已经存在于常量池中了。
String s2 = "1",这行代码是生成一个s2的引用指向常量池中的“1”对象。
结果就是 s 和 s2 的引用地址明显不同。因此返回了false。

String s3 = new String("1") + newString("1"),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。
s3.intern(),这一行代码,是将 s3中的“11”字符串放入 String 常量池中,此时常量池中不存在“11”字符串,JDK1.6的做法是直接在常量池中生成一个 "11" 的对象。
但是在JDK1.7中,常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用直接指向 s3 引用的对象,也就是说s3.intern() ==s3会返回true。
String s4 = "11", 这一行代码会直接去常量池中创建,但是发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。因此s3 == s4返回了true。

String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);// false

String s3 = new String("1") + newString("1"),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。
String s4 = "11", 这一行代码会直接去生成常量池中的"11"。
s3.intern(),这一行在这里就没什么实际作用了。因为"11"已经存在了。
结果就是 s3 和 s4 的引用地址明显不同。因此返回了false。

String str1 = new String("SEU") + new String("Calvin");
System.out.println(str1.intern() == str1);// true
System.out.println(str1 == "SEUCalvin");// true

str1.intern() == str1就是上面例子中的情况,str1.intern()发现常量池中不存在“SEUCalvin”,因此指向了str1。 "SEUCalvin"在常量池中创建时,也就直接指向了str1了。两个都返回true就理所当然啦。

String str2 = "SEUCalvin";//新加的一行代码,其余不变  
String str1 = new String("SEU") + new String("Calvin");
System.out.println(str1.intern() == str1);// false
System.out.println(str1 == "SEUCalvin");// false

str2先在常量池中创建了“SEUCalvin”,那么str1.intern()当然就直接指向了str2,你可以去验证它们两个是返回的true。后面的"SEUCalvin"也一样指向str2。所以谁都不搭理在堆空间中的str1了,所以都返回了false。

面向对象

  • 抽象类和接口的区别

    • 抽象类只能被继承并且只能继承一个,接口需要被实现但是可以实现多个
    • 抽象类中可以有成员变量;接口中的变量必须是static final的,必须是被初始化的,接口中只能有常量,不能有变量
    • 抽象类的中方法可以不是抽象的;但是接口中必须是抽象的
    • java 8 接口有default方法,可以被实现了
  • 使用场景:

    • 如果要创建不带变量和实现的基类,那么应该选择接口而不是抽象类
    • 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。
  • 重写和重载:

    • 编译期的静态多分派:overloading重载 根据调用引用类型和方法参数决定调用哪个方法(编译器)
    • 运行期的动态单分派:overriding 重写 根据指向对象的类型决定调用哪个方法(JVM)
原文地址:https://www.cnblogs.com/yankang/p/13090469.html