JAVA面试总结part1

PART1

001 char变量存储类型

Q:java 中 char 类型变量能不能储存一个中文的汉字,为什么?

java 的 char 类型变量是用来储存 Unicode 编码字符的,Unicode 字符集包含了汉字,所以 char 类型自然就能存储汉字,但是在某些特殊情况下某个生僻汉字可能没有包含在 Unicode 编码字符集中,这种情况下 char 类型就不能存储该生僻汉字了。

002 Integer与int的区别

java 的 Integer 和 int 有什么区别?

int 是 java 内置基本数据类型之一,java 为每个基本类型都提供了一个封装类,Integer 就是 int 的封装类(也叫包装类型);int 变量的默认值为 0,Integer 变量的默认值为 null,

003 switch 语句作用类型

java 的 switch 语句能否作用在 byte 类型变量上,能否作用在 long 类型变量上,能否作用在 String 类型变量上?

由于 byte 的存储范围小于 int,可以向 int 类型进行隐式转换,所以 switch 可以作用在 byte 类型变量上;由于 long 的存储范围大于 int,不能向 int 进行隐式转换,只能强制转换,所以 switch 不可以作用在 long 类型变量上;对于 String 类型变量在 Java 1.7 版本之前不可以,1.7 版本之后是可以的。

004 浮点型数据的默认数据类型

java 中 float f = 3.4; 是否正确?

不正确,3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于向下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换 float f = (float)3.4; 或者写成 float f = 3.4F; 才可以。

005 自动装箱与拆箱原理

Q: java 1.5 的自动装箱拆箱机制是编译特性还是虚拟机运行时特性?分别是怎么实现的?

  • java1.5开始的自动装箱与拆箱机制其实是编译时自动完成的。装箱阶段自动替换为valueOf(),拆箱阶段自动替换成XxxValue()方法,Integer类型的valueOf()方法参数如果时-128~127之间的值会直接返回内部缓存池中已经存在对象的引用,参数如果是其他值则返回新建对象。Double类型与Integer类型,但是没有缓存池的概念。
  • Integer、Short、Byte、Character、Long 的 valueOf 方法实现类似,而 Double 和 Float 比较特殊,每次返回新包装对象,对于两边都是包装类型的比较 == 比较的是引用,equals 比较的是值,对于两边有一边是表达式(包含算数运算)则 == 比较的是数值(自动触发拆箱过程),对于包装类型 equals 方法不会进行类型转换。

006 Integer类型的拆箱与装箱例子

Q:java 语句 Integer i = 1; i += 1; 做了哪些事情?

首先 Integer i = 1; 做了自动装箱(使用 valueOf() 方法将 int 装箱为 Integer 类型),接着 i += 1; 先将 Integer 类型的 i 自动拆箱成 int(使用 intValue() 方法将 Integer 拆箱为 int),完成加法运行之后的 i 再装箱成 Integer 类型。

007 StringBuffer,String的equals方法

Q:下面程序的运行结果是什么?

Strings1 = "abc";
StringBuffer s2 = new StringBuffer(s1);
System.out.println(s1.equals(s2));    //1,false
StringBuffer s3 = new  StringBuffer("abc");
System.out.println(s3.equals("abc"));    //2,false
System.out.println(s3.toString().equals("abc"));    //3,true
  • String源码中有对参数进行intansce of String类型判断,StringBuffer的祖先为CharSequence;StringBuffer没有重写Object的equals方法,而String重写了equals方法,实现了值比较

008 字符编码的转换

Q:怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串?

// getBytes()
String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

009 String、StringBuffer、StringBuilder 的区别

Q:String、StringBuffer、StringBuilder 的区别是什么?

  • String 是字符串常量,StringBuffer 和 StringBuilder 都是字符串变量,后两者的字符内容可变,而前者创建后内容不可变;
  • StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,线程安全会带来额外的系统开销,所以 StringBuilder 的效率比 StringBuffer 高;
  • String 的每次修改操作都是在内存中重新 new 一个对象出来,而 StringBuffer、StringBuilder 则不用,且提供了一定的缓存功能,默认 16 个字节数组的大小,超过默认的数组长度时扩容为原来字节数组的长度 * 2 + 2,所以使用 StringBuffer 和 StringBuilder 时可以适当考虑下初始化大小,以便通过减少扩容次数来提高代码的高效性。

010 String类的不可变性

Q:String类为什么不可变

  1. String 不可变是因为在 JDK 中 String 类被声明为一个 final 类,且类内部的 value 字节数组也是 final 的,
  2. 字符串池:只有当字符串是不可变时字符串池才有可能实现,字符串池的实现可以在运行时节约很多 heap 空间,因为不同的字符串变量都指向池中的同一个字符串;
  3. 安全性:如果字符串是可变的则会引起很严重的安全问题,譬如数据库的用户名密码都是以字符串的形式传入来获得数据库的连接,或者在 socket 编程中主机名和端口都是以字符串的形式传入,因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子改变字符串指向的对象的值造成安全漏洞;
  4. 线程安全:因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享,这样便不用因为线程安全问题而使用同步,字符串自己便是线程安全的;
  5. HashMap: 因为字符串是不可变的所以在它创建的时候 hashcode 就被缓存了,不变性也保证了 hash 码的唯一性,不需要重新计算,这就使得字符串很适合作为 Map 的键,字符串的处理速度要快过其它的键对象,这就是 HashMap 中的键往往都使用字符串的原因。

011 多态

Q: Java 多态的优缺点分别是什么?

  • 多态的优点:
    提高了代码的维护性(继承保证);
    提高了代码的扩展性(由多态保证);
  • 多态的缺点:
    不能使用子类的特有功能
    向下转型(把父类转换为子类型)中有可能会出现异常;
    多态程序编写:
    成员变量:编译看左,运行看左(因为无法重写);
    成员方法:编译看左,运行看右(因为普通成员方法可以重写,变量不可以);
    静态方法:编译看左,运行看左(因为属于类);

012 面对对象的特征

Q: 面向对象的特征有哪些方面?

  • 也可以说有四种,继承、封装、多态、抽象。
    1. 继承性是类的一种层次模型,其提供了一种明确表述共性的方法,对象的新类可以从现有的类中继承派生,类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
    2. 封装:封装性是把过程和数据包围起来,使得数据的访问只能通过已定义的接口,保证了对象被访问的隐私性和可靠性。
    3. 多态性是对象在不同时刻表现出来的多种状态,是一种编译时期状态和运行时期状态不一致的现象,多态性包括参数化多态性和包含多态性
    4. 抽象性是指对一类事物的高度提炼以得到共同的共性部分,抽象不需要了解全部细节,而只是一种通用的描述约束,抽象可以是过程抽象或者数据抽象。

013 重载与重写

Q: 简单说说 Java 重载与重写是什么?有什么区别?

  1. 重载: 类以统一的方式处理不同类型数据的一种手段,实质是多个具有不同参数个数或者类型的同名函数(返回值可以随意)同时存在一个类中,是一个类中多态性的一种表现
  2. 重写: 父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。
  3. 重载与重写是 Java 多态性的不同表现,重写是父类与子类之间多态性的表现,在运行时起作用(动态多态性,譬如实现动态绑定),而重载是一个类中多态性的表现,在编译时起作用(静态多态性,譬如实现静态绑定)。

014 final finally,finalize的区别

Q: 谈谈 Java 中 final、finally、finalize 的区别?

  1. final 是一个修饰符,如果一个类被声明为 final 则其不能再派生出新的子类,将变量或方法声明为 final 可以保证它们在使用中不被改变被声明为 final 的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改,被声明为 final 的方法也同样只能使用不能重载。使用 final 关键字如果编译器能够在编译阶段确定某变量的值则编译器就会把该变量当做编译期常量来使用,如果需要在运行时确定(譬如方法调用)则编译器就不会优化相关代码;将类、方法、变量声明为 final 能够提高性能,这样 JVM 就有机会进行估计并进行优化;接口中的变量都是 public static final 的。
  2. finally 用来在异常处理时提供块来执行任何清除操作,如果抛出一个异常,则相匹配的 catch 子句就会执行,然后控制就会进入 finally 块。
  3. finalize 是一个方法名,Java 允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作,这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,它是在 Object 类中定义的,因此所有的类都继承了它,子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作,finalize() 方法在垃圾收集器删除对象之前对这个对象调用的。

015 this 与super

Q: Java 中 this 和 super 的区别和应用场景?

this 为当前类的引用对象,谁调用代表谁;super 为父类存储空间标识,可以理解为父类对象,谁调用代表谁的父亲
1. this的场景
- 构造方法:通过 this 调用同类中另一个满足指定参数类型的构造方法的用发是 this(参数列表); 这个仅仅在类的构造方法中,别的地方不能这么用,同时要注意 this(参数列表); 语句只能用在子类构造方法体中的第一行。
- 变量: 函数参数或者局部变量与成员变量同名的情况下成员变量被屏蔽,this.成员变量
- 函数: 在函数中需要引用该函数所属类的当前对象时候可以直接使用this
2. super的应用场景:
- 构造方法:构造方法:在子类构造方法中要调用父类的构造方法可以用 super(参数列表); 的方式调用,参数不是必须的,同时要注意 super(参数列表); 语句只能用在子类构造方法体中的第一行。
- 变量: 当子类方法中局部变量或子类成员变量与父类成员变量同名(即子类局部变量覆盖父类成员变量)时用 super.成员变量名; 来引用父类成员变量,如果父类的成员变量没有被覆盖也可以用 super.成员变量名; 来引用父类成员变量,只是多此一举。
- 成员方法: 当子类成员方法覆盖父类成员方法(子类和父类有完全相同的方法定义)时用 super.方法名(参数列表); 的方式访问父类方法。

016 equals与hashcode

Q: Hashcode 方法的常规约定

hashCode() 的作用是为了提高在散列结构存储中查找的效率,在线性表中没有作用;
1. 程序执行期间只要对象 equals 方法比较操作所用到的信息没有被修改,则对这同一个对象无论调用多次 hashCode 方法都必须返回同一个整数。
2. 如果两个对象根据 equals 方法比较是相等的则调用这两个对象中任意一个对象的 hashCode 方法都必须产生同样的整数结果。
3. 如果两个对象根据 equals 方法比较是不相等的,则调用这两个对象中任意一个对象的 hashCode 方法不一定要产生相同的整数结果(尽量保证不相等的对象产生截然不同的整数结果是可以提高散列表性能的)。

017 异常体系

Q: java 异常有哪几种,特点是什么?

异常是发生在程序执行过程中阻碍程序正常执行的错误操作,只要在 Java 语句执行中产生异常则一个异常对象就会被创建;
Throwable 是所有异常的父类,它有两个直接子类 Error 和 Exception,其中 Exception 又被继续划分为被检查的异常(checked exception)和运行时的异常(runtime exception,即不受检查的异常);Error 表示系统错误,通常不能预期和恢复(譬如 JVM 崩溃、内存不足等);被检查的异常(Checked exception)在程序中能预期且要尝试修复(如我们必须捕获 FileNotFoundException 异常并为用户提供有用信息和合适日志来进行调试,Exception 是所有被检查的异常的父类);运行时异常(Runtime Exception)又称为不受检查异常,譬如我们检索数组元素之前必须确认数组的长度,否则就可能会抛出 ArrayIndexOutOfBoundException 运行时异常,RuntimeException 是所有运行时异常的父类。

018 throw 与throws

Q: java 中 throw 与 throws 的区别是什么?

throw 使用的位置在方法中,后面跟的异常对象实例,表示抛出异常,由方法体内语句处理,如果方法中有 throw 抛出 RuntimeException 及其子类则声明上可以没有 throws,如果方法中有 throw 抛出 Exception 及其子类则声明上必须有 throws。throws 使用的位置在方法参数小括号后面,后面跟的是一个或者多个异常类名且用逗号隔开,表示抛出异常并交给调用者去处理,如果后面根据的是 RuntimeException 及其子类则该方法可以不用处理,如果后面根据的是 Exception 及其子类则必须要编写代码进行处理或者调用的时候抛出。

019 Check异常

被检查的异常应该用 try-catch 块代码处理或用 throws 关键字抛出,不受检查的异常在程序中不要求被处理或用 throws 抛出;Exception 是所有被检查异常的基类,而 RuntimeException(是 Exception 的子类) 是所有不受检查异常的基类;被检查的异常适用于那些不是因程序引起的错误情况(如 FileNotFoundException),而不被检查的异常通常都是由于糟糕的编程引起(如 NullPointerException)。

020 Error 与Exception

Q:JAVA中error与exception

Error 表示系统级的错误,是 java 运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是 java 虚拟机抛出的。Exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序可以处理的问题。

021 异常调试处理

Q: 关于 java 中的异常处理你有啥心得或者经验?

  1. 方法返回值尽量不要使用 null(特殊场景除外),这样可以避免很多 NullPointerException 异常。
  2. catch 住了如果真的没必要处理则至少加行打印,这样可在将来方便排查问题。
  3. 避免在 finally 中使用 return 语句或者抛出异常,如果调用的其他代码可能抛出异常则应该捕获异常并进行处理,因为 finally 中 return 不仅会覆盖 try 和 catch 内的返回值且还会掩盖 try 和 catch 内的异常,就像异常没有发生一样
  4. 方法定义中 throws 后面尽量定义具体的异常列表,不要直接 throws Exception。
  5. 捕获异常时尽量捕获具体的异常类型而不要直接捕获其父类,这样容易造成混乱。
  6. 不要使用异常控制程序的流程

022 instanceof 与Obj.isInstanceof(class)

  1. instanceof

    • java 中的 instanceof 运算符用来在运行时指出对象是否是特定类的一个实例,通过返回一个布尔值来指出这个对象是否是这个特定类或者是它的子类的一个实例;
    • 用法为 result = object instanceof class,参数 result 布尔类型,object 为必选项的实例,class 为必选项的任意已定义的对象类,如果 object 是 class 的一个实例则 instanceof 运算符返回 true,如果 object 不是指定类的一个实例或者 object 是 null 则返回 false;但是 instanceof 在 java 的编译状态和运行状态是有区别的,在编译状态中 class 可以是 object 对象的父类、自身类、子类,在这三种情况下 java 编译时不会报错,在运行转态中 class 可以是 object 对象的父类、自身类但不能是子类,当为父类、自生类的情况下 result 结果为 true,为子类的情况下为 false。

      1. isInstance
    • clazz.isInstance(obj) 表明这个对象能不能被转化为这个类,一个对象是本身类的一个对象,一个对象能被转化为本身类所继承类(父类的父类等)和实现的接口(接口的父接口)强转,所有对象都能被 Object 强转,当 obj 参数为 null 时一定为 false。

023 克隆

Q:深克隆与浅克隆区别

  1. 浅克隆:被复制对象(一个新的对象实例)的所有变量都含有与原来的对象相同的值,对于基本数据类型的属性复制一份给新产生的对象,对于非基本数据类型的属性仅仅复制一份引用给新产生的对象(新实例中引用类型属性还是指向原来对象引用类型属性)。
  2. 深克隆:被复制对象(一个新的对象实例)的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象(新内存空间),而不再是原有的那些被引用的对象,换言之深度克隆把要复制的对象所引用的对象都复制了一遍,也就是在浅度克隆的基础上,对于要克隆的对象中的非基本数据类型的属性对应的类也实现克隆,这样对于非基本数据类型的属性复制的不是一份引用。

024 对象克隆实现

Q: 实现对象克隆常见的方式有哪些,具体怎么做?

常见的实现方式主要有三种。
1. 通过自己写一个克隆方法里面 new 一个同样的对象来进行 get、set 依次赋值实现深度克隆(很繁琐且易出错);
2. 通过实现 Cloneable 接口并重写 Object 类的 clone() 方法(分为深浅两种方式);
3. 通过实现 Serializable 接口并用对象的序列化和反序列化来实现真正的深度克隆;
方式2:实现浅克隆

class InfoBean implements Cloneable{
    public String name;
    public int age;

    public InfoBean clone(){
        try{
            return (InfoBean) super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
    }
}

方式2:实现深克隆

class InfoBean implements Cloneable{
    public String name;
    public int age;

    public InfoBean clone(){
        try{
            return (InfoBean) super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
    }
}

class PeopleBean implements cloneable{
    public String vipId;
    public InfoBean infoBean;

    public Object clone(){
        try{
            PeopleBean bean=(PeopleBean)super.clone();
            bean.infoBean=(infoBean)infoBean.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
    }
}

方式三: 通过Serializable接口并用对象的序列化以及反序列化实现真正的深度克隆

class CloneUtil{
    public static<T extends Serializable>T clone(T obj){
        T  cloneObj=null;
        try{
            ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
            ObjectOutputStream objOut=new ObjectOutputStream(byteOut);
            objOut.writeObject(obj);
            objOut.close();

            ByteArrayInputStream byteIn=new ByteArrayInputStream();
            ObjectInputStream objIn=new ObjectInputStream(byteIn);
            cloneObj=(T)objIn.readObject();
            objIn.close();
        }
        catch(IOException e){
            e.printStackTrace();
        }catch(ClassNotFoundException e){
             e.printStackTrace();
        }
        return cloneObj;
    }
}

class InfoBean implements Serializable{
    public String name;
    public int age;
}

class PeopleBean implements cloneable{
    public String vipId;
    public InfoBean infoBean;

    public Object clone(){
      return CloneUtil.clone(this);
    }
}

025 Java创建对象

Q:Java 创建对象的方式有哪几种?

  1. 使用 new 关键字(调用构造方法);
  2. 反射,使用 Class 类的 newInstance 方法(调用构造方法);
  3. 使用 Constructor 类的 newInstance 方法(调用构造方法);
  4. 使用 clone 方法(没有调用构造方法);
  5. 使用反序列化(没有调用构造方法)

026 抽象类与接口

Q: 抽象类(abstract class)和接口(interface)有什么区别?

  1. 首先含有 abstract 修饰符的 class 为抽象类,abstract 类不能创建实例对象,含有 abstract 方法的类必须定义为 abstract class,abstract class 类中的方法不必是抽象的,abstract class 类中定义的抽象方法必须在具体的子类中实现,所以不能有抽象构造方法或抽象静态方法,如果子类没有实现抽象父类中的所有抽象方法则子类也必须定义为 abstract 类型。
  2. 对于接口可以说是抽象类的一种特例,接口中的所有方法都必须是抽象的(接口中的方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final)。
  3. 区别:
    • 抽象类可以有构造方法;接口中不能有构造方法。
    • 抽象类中可以有普通成员变量或者常量或者静态变量;接口中没有普通成员变量和静态变量,只能是常量(默认修饰符为 publci static final)。
    • 抽象类中可以包含非抽象的普通方法和抽象方法及静态方法;接口中的所有方法必须都是抽象的,不能有非抽象的普通方法和静态方法(默认修饰符为 public abstract)。
    • 抽象类中的抽象方法访问类型可以是 public、protected 的;接口中的抽象方法只能是 public 的(默认修饰符为 public abstract)。
    • 一个子类可以实现多个接口,但只能继承一个抽象类。

027 修饰符

Q: Java 访问修饰符有哪些?有什么区别和特点?

  1. public 访问权限为当前类、同一个包、同包子类、非同包子类、其他包都可用。
  2. protect 访问权限为当前类、同一个包、同包子类、非同包子类,其他包下无法访问到。
  3. default 访问权限为当前类、同一个包,同包子类、其他包下和非同包子类无法访问到。
  4. private 访问权限为当前类,同一个包、同包子类、非同包子类、其他包都无法访问到。

028 内部类类型与特征

Q: Java 常见的内部类有哪几种,简单说说其特征?

静态内部类、成员内部类、方法内部类(局部内部类)、匿名内部类。
1. 静态内部类是定义在另一个类里面用 static 修饰 class 的类,静态内部类不需要依赖于外部类(与类的静态成员属性类似)且无法使用其外部类的非 static 属性或方法(因为在没有外部类对象的情况下可以直接创建静态内部类的对象,如果允许访问外部类的非 static 属性或者方法就会产生矛盾)
2. 成员内部类是没有用 static 修饰且定义在在外部类类体中的类,是最普通的内部类,可以看做是外部类的成员,可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员),而外部类无法直接访问成员内部类的成员和属性,要想访问必须得先创建一个成员内部类的对象然后通过指向这个对象的引用来访问;当成员内部类拥有和外部类同名的成员变量或者方法时会发生隐藏现象(即默认情况下访问的是成员内部类的成员,如果要访问外部类的同名成员需要通过 OutClass.this.XXX 形式访问);成员内部类的 class 前面可以有 private 等修饰符存在。

  1. 方法内部类(局部内部类)是定义在一个方法里面的类,和成员内部类的区别在于方法内部类的访问仅限于方法内;方法内部类就像是方法里面的一个局部变量一样,所以其类 class 前面是不能有 public、protected、private、static 修饰符的,也不可以在此方法外对其实例化使用。
  2. 匿名内部类是一种没有构造器的类(实质是继承类或实现接口的子类匿名对象),由于没有构造器所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调,匿名内部类在编译的时候由系统自动起名为 OutClass$1.class,一般匿名内部类用于继承其他类或实现接口且不需要增加额外方法的场景(只是对继承方法的实现或是重写);匿名内部类的 class 前面不能有 pravite 等修饰符和 static 修饰符;匿名内部类访问外部类的成员属性时外部类的成员属性需要添加 final 修饰(1.8 开始可以不用)。

029 匿名内部类

Q: 开发中使用 Java 匿名内部类有哪些注意事项(经验)?

  1. 使用匿名内部类时必须是继承一个类或实现一个接口(二者不可兼得且只能继承一个类或者实现一个接口)。
  2. 匿名内部类中是不能定义构造函数的,如需初始化可以通过构造代码块处理。
  3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
  4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  5. 匿名内部类不能是抽象类,必须要实现继承的类或者实现接口的所有抽象方法。

030 内部类好处

Q: Java 中内部类有什么好处

  1. 隐蔽 内部类可以很好的实现隐蔽,一般的非内部类,是不允许有 private 与 protected 等权限的,但内部类(除过方法内部类)可以通过这些修饰符来实现隐藏。
  2. 内部类拥有外部类的的访问权限(分静态非静态情况),通过这一特性可以比较好的处理类之间的关联性,将一类事物的流程放在一起内部处理。
  3. 通过内部类可以实现多重继承,java 默认是单继承,我们可以通过多个内部类继承实现多个父类,接着由于外部类完全可访问内部类,所以就实现了类似多继承的效果。
  4. 通过内部类可以避免修改接口而实现同一个类中两种同名方法的调用(譬如你的类 A 中有一个参数为 int 的 func 方法,现在类 A 需要继承实现一个接口 B,而接口 B 中也有一个参数为 int 的 func 方法,此时如果直接继承实现就会出现同名方法矛盾问题,这时候如果不允许修改 A、B 类的 func 方法名则可以通过内部类来实现 B 接口,因为内部类对外部类来说是完全可访问的)。

031 引用类型

Q:Java 中的几种引用类型及其区别是什么?

分别是强引用、软引用、弱引用、虚引用,其主要不同点体现在 GC 和使用上面。
1. 强引用就是类似 Object obj = new Object() 这样的引用,如果一个对象具有强引用就不会被垃圾回收器回收,
2. 软引用用来描述一些还有用但是非必须的对象(譬如缓存),其使用 SoftReference 来创建,在使用软引用时如果内存空间足够则软引用就能继续被使用而不会被垃圾回收器回收,只有在内存不足时软引用才会被垃圾回收器回收。
3. 弱引用也是用来描述非必须对象的,它的强度比软引用更弱一些,其使用 WeakReference 来创建,具有弱引用的对象拥有的生命周期更短暂,当 JVM 进行垃圾回收时一旦发现弱引用对象则无论当前内存空间是否充足都会将弱引用回收。
4. 虚引用就是形同虚设,它是最弱的一种引用关系,其使用 PhantomReference 来创建,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,奖这个虚引用加入引用队列,一个对象是否有虚拟用的存在完全不会对其生存时间构成影响,也无法通过一个虚引用来取得一个对象实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

032 transient

Q:你对 Java 的 transient 关键字理解

  1. 对于不需要被序列化的属性就可以通过加上 transient 关键字来处理。一旦属性被 transient 修饰就不再是对象持久化的一部分,该属性的内容在序列化后将无法获得访问,transient 关键字只能修饰属性变量成员而不能修饰方法和类(注意局部变量是不能被 transient 关键字修饰的),属性成员如果是引用类型也需要保证实现 Serializable 接口,
  2. 对于transient修饰的属性在不删除修饰符的情况下让其可以序列化,需要采用实现 Serializable 接口情况下通过 writeObject() 与 readObject()方法进行自定义序列化的机制。

033 泛型概述

Q: ava 的泛型是什么?有什么好处和优点?JDK 不同版本的泛型有什么区别?

  1. 泛型是 Java SE 1.5 的新特性,泛型的本质是参数化类型,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法
  2. 在 Java SE 1.5 之前没有泛型的情况的下只能通过对类型 Object 的引用来实现参数的任意化,其带来的缺点是要做显式强制类型转换,而这种强制转换编译期是不做检查的,容易把问题留到运行时,所以 泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException。
  3. JDK 1.5 引入了泛型来允许强类型在编译时进行类型检查;JDK 1.7 泛型实例化类型具备了自动推断能力

034 泛型原理

  1. 泛型是通过类型擦除来实现的,编译器在编译时擦除了所有泛型类型相关的信息,所以在运行时不存在任何泛型类型相关的信息,譬如 List 在运行时仅用一个 List 来表示,这样做的目的是为了和 Java 1.5 之前版本进行兼容。
  2. 泛型擦除具体来说就是在编译成字节码时首先进行类型检查,接着进行类型擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法),接着如果类型擦除和多态性发生冲突时就在子类中生成桥方法解决,接着如果调用泛型方法的返回类型被擦除则在调用该方法时插入强制类型转换

035 泛型带来的问题

  1. 泛型中参数化类型无法支持继承关系
  2. 泛型与多态的冲突,其通过子类中生成桥方法解决了多态冲突问题
  3. 泛型读取时会进行自动类型转换问题,所以如果调用泛型方法的返回类型被擦除则在调用该方法时插入强制类型转换。
  4. 泛型类型参数不能是基本类型
  5. 无法进行具体泛型参数类型的运行时类型检查
  6. 不能抛出也不能捕获泛型类的对象

036 泛型数组

Q: 如何正确的初始化泛型数组实例?

  1. 无论通过 new ArrayList[10] 的形式还是通过泛型通配符的形式初始化泛型数组实例都是存在警告的,也就是说仅仅语法合格,运行时潜在的风险需要我们自己来承担,因此那些方式初始化泛型数组都不是最优雅的方式
  2. 用到泛型数组的场景下应该尽量使用列表集合替换,此外也可以通过使用 java.lang.reflect.Array.newInstance(Class componentType, int length) 方法来创建一个具有指定类型和维度的数组,

037 泛型擦除内容

Q: 泛型擦除到底擦除了哪些信息?

泛型擦除其实是分情况擦除的,不是完全擦除,一定要消除这个误区。
Java 在编译时会在字节码里指令集之外的地方保留部分泛型信息,泛型接口、类、方法定义上的所有泛型、成员变量声明处的泛型都会被保留类型信息,其他地方的泛型信息都会被擦除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值