字节码文件:
- JVM可以理解的代码就叫做字节码 .class文件
类加载的入口:
- 是 .class文件
JDK和JRE区别:
- JRE是Java的运行环境,包括JVM和核心类库
- JDK是Java的开发工具包,比JRE多了javac编译器和工具(如jdbc)
Java和C++的异同:
- 都是面向对象语言,但是Java不提供指针访问内存、Java类是单继承而C++是多继承、Java有自动内存管理机制
Java为编译型同时又为解释型语言:
- 先通过javac编译生成 .class字节码文件,然后交给JVM解释运行
- Java源代码 → 编译器 → .class字节码文件 → JVM → JVM解释器 → 二进制机器码 → 程序运行
字符型常量和字符串常量的区别:
- 字符型常量:单引号,相当于一个整形值(ASCII),占用2个字节
- 字符串常量:双引号,代表一个地址值,占用若干字节
变量大小
标识符和关键字:
- 标识符就是指变量或者类名,关键字就是public/if/while之类的
continue:
- 跳出当前一次的循环,直接进入下一次循环
break:
- 跳出整个循环体,直接执行循环体下面的语句
return:
- 跳出所在方法
Java泛型:
- Java的泛型是伪泛型,因为在编译期间泛型类型会被擦除掉,转换为具体的类型,这就是所谓的泛型擦除
==:
- 基本类型中(“==”比较的是变量的值)
- 引用类型中(“==”比较的是引用对象的地址值,如对象、数组)
equals():
- “equals”是Object中的方法,所以说只有对象可以使用equals进行比较,Object中的equals方法比较的是对象的地址值,也就是“==”,但是不同的类可以重写equals方法进而比较类是否相等(如String类)
hashcode():
- “hashcode”是Object中的方法,用于返回对象在「哈希表」中的索引位置,是一个16位长度的int整数,Object中的hashcode()是native本地方法,是通过C++将对象地址转换为int整数返回的
为什么要有hashcode:
- 用HashSet集合进行举例,每一次添加元素都会比较元素是否为同一个元素,先比较hashcode,因为hashcode复杂度为O(1),计算很快,如果hashcode相同再调用equals()方法,这样可以大大减少equals()方法的调用次数,因为equals()计算比hashcode慢很多
为什么重写equals()方法后要重写hashcode()方法:
- 因为两个对象相等,hashcode也一定相等,但是hashcode相等,不一定对象就一定相等,如果没有重写hashcode()方法,对象不可能会相等
HashCode注意事项:
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的。因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重hashCode()则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
基本类型
8大类型及其常量池:
- 8大类型部分实现了常量池(Byte,Short,Integer,Long,Character,Boolean),Float/Double没有实现常量池,前4种的类型实现了[-128,127]的缓存数据池,Character创建数值[0,127]范围的缓存数据池,Boolean直接返回True/Flase,如果超出了缓存数据池则会创建新对象
Java值传递:
- Java只有值传递
- 对于基本类型来说,作为方法的参数,只是复制一个变量同时给这个变量赋予基本类型的值进行使用
- 对于引用类型来说,作为方法的参数,将对象或数组的「地址」也就是「引用变量」复制一份,将这个值作为方法的参数,而不是直接将引用作为方法的参数。所以说看上去像是引用传递,但其实Java都是值传递
重载与重写:
- 重载(发生在同一个类中):
- 方法名必须相同
- 方法参数的类型/数量/顺序必须有一个不同
- 返回值类型和方法的修饰符可以不同
- 重写(发生在父类和子类中):
- 方法名必须相同
- 方法参数必须相同
- 返回值类型范围小于等于父类
- 抛出的异常范围小于等于父类
- 方法的修饰符范围大于等于父类
- 如果父类方法访问修饰符为private/final/static,则子类不能重写该方法
- 构造方法不可重写
深拷贝和浅拷贝:
- 浅拷贝:是指对于基本类型为值传递,对于引用类型进行原引用地址的值传递
- 深拷贝:是指对于基本类型为值传递,对于引用类型,创建一个新的对象,并复制其内容,再让引用类型指向其新创建的对象
- Object类中的clone()为浅拷贝,如果想进行深拷贝,则需要对于clone的对象中的引用类型再调用一次clone(),或者进行序列化之后再反序列化回来
类中定义一个无参构造且无作用的方法的意义:
- 在子类执行构造方法之前,如果没有使用super()调用父类的特定构造方法,就会默认调用父类的无参构造方法,而父类如果只定义了有参构造方法,而没有无参构造方法且子类又没有在子类构造方法调用之前调用super()则会编译发生错误
成员变量和局部变量:
- 成员变量:属于类的,是对象的一部分,随着对象的创建销毁而创建销毁,如果没有赋初值会赋予默认的值
- 局部变量:属于方法或是方法中定义的参数,是方法的一部分,随着方法的调用而自动消失(JVM虚拟机栈中的入栈和出栈),不会自动赋予默认值
封装、继承、多态:
- 封装:封装是指对于一个类会将必要的信息封装进类内部,使得外部不能直接访问,但是可以提供一些方法进行间接访问
- 继承:继承是指不同类之间会存在相同的地方,可以让子类继承父类的一些特点,提高代码的重用(子类拥有父类的所有的非private的属性和方法,但是父类私有的属性或方法是无法访问的,只是拥有)
- 多态:多态是指一个对象具体多种状态,具体指「父类引用指向子类实例」(左编译右运行,引用类型调用的方法是哪个类的方法,在运行时才能确定、多态不能调用只在子类中有而父类没有的方法、如果子类重写了父类的方法,调用的是子类方法,反之为父类方法)
- 实现多态的2种方法:
- 继承
- 接口
- 实现多态的2种方法:
接口与抽象类:
- 抽象类可以存在普通成员函数,而接口只能存在public abstact方法
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型
- 抽象类只能继承一个,而接口可以实现多个
- 抽象类是一般类的部分抽象,而接口是一般类的完全抽象
- 抽象类和接口都不能实例化
- 抽象类是对类的抽象,是一种模版设计;而接口是对行为的抽象,是一种行为的规范
Object常用8个方法:
- getClass():获取当前对象的类型
- toString():输出对象的成员变量,通常重写
- hashcode():获取对象的hashcode值
- clone():浅拷贝当前对象
- wait():将当前线程等待该对象的锁,wait()一直等待,直到获取锁为止,也可以wait(Long)设定timeout
- notify():唤醒在该对象上等待的某个线程
- notifyall():唤醒在该对象等上等待的所有线程
- finalize():用于释放资源,进行GC,不能直接调用,JVM底层自动调用
NIO:
- 传统IO:是一个字节地处理数据,只能是阻塞的
- NIO:是以块(Buffer缓冲区)的形式处理数据,可以实现非阻塞
- IO的实际场景:文件IO、网络IO
- NIO的核心组成部分
- Selector(选择器):检查多个Channel的状态变更情况,将请求交给某个空闲的线程处理
- Channel(管道):运输数据的载体
- Buffer(缓冲区):存储数据的地方
String、StringBuffer、StringBuilder区别:
- String:final关键字修饰,不可变,每次对String类型的值进行改变的时候,都会产生新的String对象,然后将引用指向新的String对象。而StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象的引用。String有字符串常量池
- StringBuffer和StringBuilder(可变长字符串)都是在原对象操作的
- StringBuffer是线程安全的,StringBuilder是线程不安全的
- StringBuffer内的方法都是synchronized修饰的
- 性能:
- StringBuilder > StringBuffer > String
自动装箱和拆箱:
- 装箱:将基本类型用它们对应的引用类型包装起来
- 拆箱:将包装类型转换为基本数据类型
在一个静态方法内调用一个非静态成员为什么是非法的:
- 静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员
final关键字:
- final修饰变量:
- 修饰「基本数据类型」的变量,则其数值一旦在初始化之后便不能更改
- 修饰「引用类型」的变量,则在对其初始化之后便不能再让其指向另一个对象,但是引用指向的对象是可以变的
- 修饰「类变量」,只能在static{}静态代码块中指定初始值,或者在声明类变量时指定初始值
- 修饰「局部变量」,可以在非静态代码块、声明该变量、构造器中指定初始值
- final修饰类:
- 类不能被继承
- final类种的所有成员方法都会被隐式地指定为final方法
- final修饰方法:
- 子类无法重写父类的final方法
- 类中所有的private方法都隐式地指定为final
Java异常处理:
- Java中所有的异常都有一个共同的父类 java.lang包下的Throwable类
- Throable类有2个重要的子类:Exception(异常)、Error(错误)
- Exception能被程序本身处理(try-catch),不会导致程序停止
- RunTimeException(运行时异常)
- CheckedException(检查异常)
- Error是无法处理的错误,一旦出现,程序被迫停止运行(只能尽量避免)
transient关键字:
- 阻止实例中那些用此关键字修饰的变量序列化
- 当对象被反序列化时,被transient修饰的变量值不会被持久化和回复
- transient只能修饰变量,不能修饰类和方法
Java IO流:
- InputStream/OutputStream:字节输入输出流的父类
- Reader/Writer:字符输入输出流的父类
BIO、NIO、AIO区别:
- BIO(Blocking IO):同步阻塞IO,数据的读写必须阻塞在一个线程内等待其完成
- NIO(Non-Blocking IO):同步非阻塞IO,提供Channel,Selector,Buffer等抽象
- AIO(Asynchronous IO):异步非阻塞IO,异步IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会阻塞在那里,当后台处理完成,OS会通知相应的线程进行后续的操作
String s = new String("abc")创建了几个实例:
- 栈中 s 引用
- 堆中新String对象
- 指向常量池中的 “abc”
String对象的intern()方法:
- String对象的intern()方法首先会检查「字符串常量池」是否存在“abc”,如果存在则返回该「字符串的引用」,如果不存在,则把“abc”添加到字符串常量池,并返回该「字符串的引用」