1.java静态变量,代码块,和静态方法执行顺序是什么
代码块分三种:static静态代码块,构造代码块,普通代码块
代码块执行顺序:静态代码块 -> 构造代码块 -> 构造函数 -> 普通代码块
继承中代码块执行顺序:父类静态块 -->子类静态块 -> 父类代码块 -> 父类构造器 -> 子类代码块 -> 子类构造器
2.多态
多态:指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,使得同一个属性或方法在父类及其各个子类中具有不同的含义。
- 编译时多态(静态多态):例如重载,编译时多态在编译时就已经确定,运行时调用的是确定的方法
- 运行时多态(动态多态):例如重写,编译时不确定调用哪个具体方法,一直延迟到运行时才确定
多态三个必要条件:继承、重写、向上转型
3.值传递,引用传递
- 值传递:指在方法调用时,传递的参数是按值的拷贝,传递后就互不相关了
- 引用传递:在方法调用时,传递的参数是按引用进行传递,传递的是引用的地址,即变量所对应的内存空间的地址,传递的是值的引用,即传递前和传递后都指向同一个引用(即同一个内存空间)
基本类型作为参数被传递时肯定是值传递,引用类型作为参数被传递时也是值传递,只不过值为对应的引用
4.String设计成不可变的:设计考虑,效率优化,安全性
- 便于实现字符串池:当初始化一个String变量时,如果该字符串已经存在了,就不会创建一个新的字符串变量,而是返回已经存在的字符串的引用。
- 多线程安全
- 避免安全问题
- 加快字符串处理速度:string不可变,保证了hashcode的唯一性
5.字符串常量池
java中常量池
-
全局字符串常量池:jvm为了提升性能,减少内存开销,避免字符重复创建
-
class文件常量池:当程序运行到某个类时,class文件中的信息就会被解析到内存的方法区里的运行时常量池中。每个类都有一个运行时常量池
-
运行时常量池:class常量池是在编译后每个class文件都有的,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是 常量池*(constant pool table)*,用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。*字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
6.intern()函数
- 作用:将对应的符号常量进入特殊处理,
- JDK1.6:先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,则将该字符串常量加入到字符串常量区,即在字符串常量区建立该常量
- JDK1.7:先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,说明该字符串常量在堆中,则处理是把堆区该对象的引用加入到字符串常量池中,以后别人拿到的是该字符串常量的引用,实际存在堆中
7.获取反射中的Class对象
- Class clz = Class.forName(“java.lang.string”);//类路径
- Class clz = String.class;//类名.class,只适合在编译前就知道操作的class
- String str = new String(“Hello”); Class clz = str.getClass();//对象名.getClass()
- 如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
8.反射API
- Class类:反射的核心类,可以获得类的属性,方法等信息
- Field类:java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类中的属性值
- Method类:java.lang.reflec包中的类,表示类的方法,他可以用来获取类中的方法信息或者执行方法
- Constructor类:java.lang.reflec包中的类,表示类的构造方法
9.反射使用步骤
- 获取想要操作的类的Class对象,反射的核心,通过Class对象可以任意调用类的方法
- 调用Class类中的方法,反射的使用阶段
- 使用反射API来操作这些信息
package com.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用
Class clz = Class.forName("com.example.Apple");
Method setPrice = clz.getMethod("setPrice", int.class);
Constructor appConstruct = clz.getConstructor();
Object appleObj = appConstruct.newInstance();
setPrice.invoke(appleObj, 14);
Method getPrice = clz.getMethod("getPrice");
System.out.println("Apple price:" + getPrice.invoke(appleObj));
}
}
10.反射应用
- 让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能
- 让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法
- 测试时可以利用反射API访问类的私有成员,保证测试代码覆盖率
例如:
- JDBC数据库的链接
- spring框架的使用,最经典的xml配置模式
11.反射机制原理
Class actionClass = Class.forName(“MyClass”);
Object action = actionClass.newInstance();
Method method = actionClass.getMethod(“myMethod”, null);
method.invoke(action, null);
前两行实现了类的装载,链接和初始化,newInstance方法实际上也使用了反射调用了方法
后两行实现了从class对象中获取到method对象然后执行反射调用
- 反射获取类实例Class.forName(),将实现交给了jvm去加载,先获取ClassLoader,然后调用native方法,获取信息,加载类则是回调java.lang.ClassLoader。最后,jvm回调ClassLoader进类加载。
- newInstance()主要做三件事
- 权限检测,如果不通过直接抛出异常
- 查找无参构造器,并将其缓存起来
- 调用具体方法的无参构造方法,生成实例并返回
- 获取Method对象
- Class对象在加载类时由JVM构造的,JVM为每个类管理一个独一无二的Class对象,这份Class对象里维护着该类的所有Method,Field,Constructor的cache,这份cache也被称作根对象
- 调用invoke方法
12.泛型
将类型参数化,再编译时才确定具体的参数。泛型类,泛型接口,泛型方法
原理:类型擦除 - 使用泛型的时候加上的类型参数,编译器在编译的时候去掉类型参数。
泛型只存在于编译阶段,而不存在于运行阶段。在编译后的class文件中,是没有泛型这个概念的
大部分情况下,泛型类型都会以Object进行替换。
public class caculate{
private T num;
}
这种情况的泛型类型,num会被替换为String 而不再是Object
泛型翻译:编译器还会为某些返回值为限定的泛型类型的方法进行强制类型转换,由于类型擦除,返回值为泛型类型的方法都会擦除成Object类型,当这些方法被调用后,编译器会额外插入一行checkcast指令用于强制类型转换。
13泛型中的限定通配符和非限定通配符
- 限定通配符:对类型进行限制。
- extends T>它通过确保类型必须是T的子类来设定类型的上界
- <?super T>它通过确保类型必须是T的父类来设定类型的下界。
- 泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误
- 非限定通配符:?可以用任意类型来替代。
例如
- List extends T>: 可以接受任何继承自T的类型的List
- List super T>:可以接受任何T的父类构成的List
14.ArrayList 和 ArrayList是否相等
相等,他们的Class类型都是一样的,都是ArrayList.class
String 和Integer体现在类编译的时候,JVM进行类编译时,会进行泛型检查,如果一个集合被声明成String类型,那么往集合存取数据的时候就会对数据进行判断,从而避免存入或取出错误的数据。
15.Java序列化和反序列化
- 序列化:把Java对象转换为字节序列的过程。核心作用:对象状态的保存与重建
- 对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
- 反序列化:把字节序列恢复为java对象的过程。
- 客户端从文件或网络上获取序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象
- 作用
- 对象序列化可以实现分布式对象
- java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据
- 序列化可以将内存中的类写入文件或数据库中
- 对象、文件、数据、有许多不同的格式,很难统一传输和保存
- 序列化实现方式
- 实现Serializable接口或者Externalizable接口
- 类通过实现java.io.Serializable接口以启用其序列化功能,序列化接口没有方法或字段,仅用于标识可序列化语义。
- Externalizable继承自Serializable 接口中定义了两个抽象方法writeExternal 和readExternal,需重写这两个方法
- serialVersionUID:用来表明类的不同版本间的兼容性。Java序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性。
- 显示指定:不显示指定,jvm在序列化时会根据属性自动生成一个serialVersionUID,如果显示指定,则值为显示指定的值
- 如果不显示指定问题:如果类写完不再修改,不会有问题,但类不断迭代,一旦修改,就对象反序列化就会报错,一般显示指定一个serialVersionUID,值多少无所谓,只要不变就行。
- 修改:序列化类新增属性时,不要修改,避免反序列化失败;如果完全不兼容升级,避免反序列化混乱,请修改。
- transient:如果有些字段不想序列化,使用transient。 作用:控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值被设为初始值,如int为0,对象为null。transient只能修饰变量,不能修饰类和方法
- 静态变量不会被序列化:静态变量优先于对象存在,随着类的加载而加载,所以不会被序列化
16.error 和 exception 都是Throwable类中的子类
- exception:程序本身可以处理的异常,通过catch来捕获,遇到这种错误,应对其进行处理,使应用程序可以继续正常运行,
- 运行时异常:非受检查异常
- 包括RuntimeException类及其子类,表示JVM在运行期间可能出现的异常,eg:NullPointException,NumberFormatException,IndexOutOfBoundsException,ClassCastException,ArrayStoreException等
- 非运行时异常:受检查异常
- 是Exception 中除RuntimeException及其子类之外的异常,常见的异常有:IO相关的异常,ClassNotFoundexception,SQLException等
- 区别:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检查异常,否则就选择非受检查异常。
- error:程序无法处理的错误,没办法通过catch来进行捕获。
17.throw 和throws
- throw 用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出
- throws用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表,一个方法用throws标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常代码,否则也要在方法签名中用throws关键字声明相应的异常。
String s = "abc";
String s1 = "ccc";
List ss = new ArrayList<>();
ss.add(s);
ss.add(s1);
for (String s2 : ss) {
System.out.println(s2);
try {
System.out.println(Double.parseDouble(s2));
}catch (NumberFormatException e){
System.out.println("xxx");
}
}
// for (String s2 : ss) {
// if (s2.equals("abc")){
// throw new NumberFormatException();
// } else {
// System.out.println(s2);
// }
// }
18.NoClassDefFoundError 和 ClassNotFoundException
- NoClassDefFoundError :Error类型异常,jvm引起的,不应该尝试捕获这个异常,引起该异常的原因是jvm或classLoader尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但运行时却找不到了,可能是编译后被删除了等原因导致的。
- ClassNotFoundException:受检查异常,需要显示的try-catch对其进行捕获和处理,或在方法签名中用throws关键字进行声明。当使用Class.forName,ClassLoader.loadClass动态加载类到内存时候,通过传入路径参数没有找到该类,就会抛出该异常,另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。
19.常见异常
- java.lang.IllegalAccessError:违法访问错误。当一个应用视图访问,修改某个类的域或调用其方法,但又违反域或方法的可见性声明,则抛出该异常。
- java.lang.InstantiationError:示例化错误,当一个应用试图通过java的new操作符构造一个抽象类或接口时抛出该异常
- java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误
- java.lang.StackOverflowError:堆栈溢出错误,当一个应用递归调用的层次太深二导致堆栈溢出或者陷入死循环时抛出该错误。
- java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常,该异常经常被称为强制类型转换异常
- java.lang.ClassNotFoundException:找不到类异常,当应用试图根据字符串形式的类名构造类,而在遍历CLASSPATH之后找不到对应名称的class文件时,抛出该异常
- java.lang.ArithmeticException:算术条件异常,eg:整数除零等
- java.lang.ArrayIndexOutOfBoundsException:数组索引越界异常,当对数组的索引值为负数或者大于等于数组大小时抛出
- java.lang.IndexOutOfBoundsException:索引越界异常,当访问某个序列的索引值小于0或大于等于序列大小时抛的异常。
- java.lang.InstantiationException:实例化异常,当试图通过newInstance方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常
- java.lang.NosuchFieldException:属性不存在异常,当访问某个类不存在的属性时抛出该异常
- java.lang.NoSuchMethodException:方法不存在异常,当访问某个类的不存在的方法时抛出该异常
- java.lang.NullPointerException:空指针异常
- java.lang.NumberFormatException:数字格式异常
- java.lang.StringIndexOutOfBoundsException:字符串索引越界异常
20.JVM处理异常
方法异常-> 创建一个异常对象(包含异常名称,异常描述和异常发生时应用程序的状态),转交给JVM(抛出异常)
一系列的方法调用,最终才进入抛出异常的方法(调用栈)
jvm顺着调用栈查看是否有可以处理异常的代码,
- 有,调用异常处理代码,把发生的异常传递给他,
- 没有,jvm将该异常转交给默认的异常处理器,默认异常处理器打印出异常信息并终止应用程序
21.IO流
- 分类
- 流方向:输入流,输出流
- 实现功能
- 处理数据的单位
- 字节流:InputStream,OutputStream
- 字符流:Reader,Writer
- 字节流转为字符流
- 字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象
- 字节输出流转字符输出流通过OutputStreamWriterr实现,该类的构造函数可以传入OutputStream对象
- 字符流和字节流区别
- 读写的时候字节流是按字节读写,字符流按字符读写
- 字节流适合所有类型文件的数据传输,因为计算机字节(Byte)是电脑中表示信息含义的最小单位,字符流只能够处理纯文本数据,其他类型数据不行,但是字符流处理文本要比字节流处理文本要方便
- 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流
- 只是读写文件,和文件内容无关时,一般选择字节流。
22.阻塞IO 和 非阻塞IO
IO:对硬盘的读写、对socket的读写以及外设的读写
用户线程发起一个IO操作,内核查看读取数据是否就绪,如果没有就绪
- 阻塞IO:一直等待,直到数据就绪
- 非阻塞IO:返回一个标志信息告知用户线程当前要读的数据没有就绪
就绪后,将数据拷贝到用户线程。
java中传统的IO都是阻塞IO
23.BIO、NIO、AIO
- BIO:同步并阻塞。一个连接一个线程,客户端有连接请求,服务器启动一个线程处理,适用于连接数目小且固定的架构,对服务器资源要求比较高。
- NIO:同步并非阻塞。一个请求一个线程,客户端发送连接请求注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程处理。适用于连接数目多且连接比较短的架构
- AIO:异步并非阻塞。客户端IO请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。一个有效请求一个线程,适用于连接数目多且连接比较长的架构
24.Java IO设计模式
- 适配器模式:
- Reader reader = new InputStreamReader(inputStream)
- 装饰器模式
- new BufferedInputStream(new FileInputStream(inputStream))