1.1 特点

简单易学
面向对象(三特点)
平台无关
可靠
安全
支持多线程
支持且方便网络编程
编译与解释并存
面向对象易于复用、扩展和维护

1.2 面向过程性能比面向对象高的原因 P24

Java半编译语言——
最终执行代码不是可直接被CPU执行的二进制机械码

1.3 JVM JDK JRE

JVM

运行字节码文件(.class)的虚拟机,即在机器和编译程序之间加入一层抽象的虚拟的机器,在任何平台上都提供给编译程序一个共同的接口
目的:不同系统,相同结果
java源代码——编译器——jvm可执行的java字节码(即虚拟指令)——jvm——jvm中的解释器——机器可执行的二进制机器码——程序运行
关键步骤示意图
image.png
注意:.class->机器码 JVM类加载器先加载字节码文件,然后通过解释器逐行解释执行,慢。
热点代码——JIT编译器,完成第一次编译,对应的机器码保存下来,下次直接使用。
JDK9引入AOT编译器

什么是字节码,采用字节码的好处

供虚拟机理解的代码为字节码(.class文件)
通过字节码的方式,解决传统解释型语言执行效率低的问题,又保留解释型语言可移植的特点
不针对特定机器,java程序无需重新编译,便可在多种不同计算机运行

JDK和JRE

JDK = JRE+编译器(javac)+工具(javadoc+jdb) 创建、编译 程序
JRE = JVM+Java类库+java命令+其他基础构件 编译
image.png

1.4 Java和C++区别

指针
类单继承、接口多继承
自动内存管理机制
无结束符(\0)

1.5 字符型和串常量区别、各类型存储空间 P28

形式:单双引号
含义:整型值(ASCII),可表达式运算 vs 地址值
内存大小:char 2字节 vs 若干字节
image.png
byte:占1个字节
char:占2个字节
short:占2个字节
int:占4个字节
long:占8个字节
float:占4个字节
double :占8个字节
boolean:单独使用占4个字节,数组中作为元素占1个字节
一个字节占8位。

理由来源是《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节
虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗。大多数人都会很自然的这样去想,我同样也有这个疑问,经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。

1.6 重载overload和重写override的区别 P28

重载:发生在同一个类中,方法名必须相同,参数类型、个数或顺序不同,返回值和访问修饰符可不同
重写:两同两小一大
image.png

1.7 三大特性:封装 继承 多态

封装

1.javabean:属性私有化 + 外界访问属性的方法get、set
2.orm框架:操作数据库,不需关心链接怎么建立,sql如何执行,只需引入mybatis,调方法即可
(即只需调用,无需关注实现细节)

继承

复用代码
3点:
1.父类的私有属性和方法子类 只是拥有
2.子类可拥有自己的属性方法,扩展
3.自己方式实现父类方法(重写),改变

多态

引用变量指向的具体类型和方法调用在运行期确定
基于对象所属类的不同,外部对同一方法的调用,实际执行的逻辑不同
父类类型 变量名 = new 子类对象;
变量名.方法名();
这样之后换个子类,也不用修改第二句。易于维护。
两种实现方式
1.继承(方法重写)
2.接口(实现并覆盖方法)

1.8 String、StringBuffer、StringBuilder P31

String

final 不可变 常量 线程安全 char[] 9.0以后byte[]

StringBuffer

同步锁 线程安全

StringBuilder

线程不安全
提升10%性能

面试回答使用场景

如果需要经常改变字符串,则用后两个。
优先使用StringBuilder,当变量是共享变量或在多线程环境下要保持结果正确时,使用StringBuffer。

1.9 无参构造器的用途 P33

子类若没用super()调用父类特定构造器,会自动调用无参,若无,编译报错

1.10 抽象类和接口的区别

5点
接口方法默认public,方法不在接口中实现(8.0开始有默认实现)vs 抽象类有非抽象方法
接口类的成员变量只能是public static final vs 抽象类的成员变量可以各种类型
多接口、一个抽象类
抽象方法可以public、protect和default(为了重写而设计,不能有private)
设计层面:模板设计 vs 行为规范
接口的设计目的:对类的行为进行约束,强制要求不同的类具有相同的行为。只约束行为的有无,不对如何实现行为进行限制。对行为的抽象 like a
抽象类的设计目的:代码复用。把不同类具有的某些相同行为且一部分行为的实现方式一致时,派生于一个抽象类。对类本质的抽象 is a
使用场景:
关注一个事物的本质的时候,用抽象类
关注一个操作的时候,用接口

抽象方法必在抽象类中 错的,可以在接口中,抽象类中不一定有抽象类

1.11 成员变量和局部变量

成员变量

属于类
可被public、private、static修饰(final两者都可)
内存存储方式——?、堆内存
生存时间——随对象创建 vs 随方法调用
自动以类型默认值赋值

局部变量

基本数据类型——栈内存
引用——指向堆内存对象的引用 或 指向常量池中的地址

1.12 ==与equals P36

==

判断地址
基本数据类型:值
引用:地址

equals

判断内容
类没有覆盖的时候,等价于==
通常覆盖此方法,用于比较内容
String中的equals已被覆盖,所以比的是对象的值

1.13 hashCode和equals 为什么重写equals一定要重写hashCode方法 P37

hashCode()

定义在Object.java中,java中的任何类都包含hashcode()函数
默认:默认对堆上对象产生独特值,未重写前个个不同
返回:int整数
作用:确定对象在哈希表的索引地址(哈希表存键值对,根据键快速检索出对应的值)
使用目的:缩小查找成本
问题:哈希碰撞,哈希值相等不一定内容相同,与数据值域分布特性相关

equals()

判断是否真的相等(按照指定规则),重写时要重写hashCode()

解答:
默认各个对象的hashCode各不相同,重写hash使得不同对象的hashCode相等成为可能,两个hashCode相等的对象,进一步使用equals判断内容是否相等。因此在equals之前需重写hashCode。

1.14 Java中只有值传递 P38

方法得到的是所有参数值的一个拷贝,即方法不能改变传递给它的任何参数变量的内容
基本数据类型显然。
注意:引用类型可以通过方法改变对象参数状态。但是这不是引用调用
因为:改变对象参数时,实际是拷贝了新的一对引用变量指向了相同的地址,利用拷贝的引用变量操作,原本传入的引用变量仍然没有改变其指向,故仍是值传递。

1.15 线程、程序、进程 P43 协程(在多线程18)

进程是操作系统资源分配的基本单位,线程是CPU任务调度和执行的基本单位
进程是系统运行的基本单位,是程序的一次执行过程
线程是比进程更小的执行单位,一个进程至少一个线程,可以包含多个线程
内存分配好好学习
资源开销

线程

多个线程共享同一块内存空间和一组系统资源

程序

含有指令数据的文件——静态的代码

进程

程序的一次执行过程,运行程序的基本单位动态
各进程独立

1.16 线程的生命周期(5)/哪些状态

image.png
image.png
new——start()——ready、running(java称之为runnable)
wait:需其他线程通知,才返回
time_wait:超时限制,时间到达后自动返回

image.png
image.png

理解简记

五种状态:创建、就绪、运行、阻塞、死亡
1.创建线程对象
2.调用线程对象的start()方法,等待CPU的使用权
3.就绪的线程获取了CPU的使用权,执行代码
4.线程因某种原因放弃了CPU使用权,暂停运行
5.线程执行完或因异常退出了run方法,结束生命周期

阻塞分三种:
等待阻塞wait方法,线程被放入“等待池”,后续需notify、notifyAll唤醒 这三方法都是Object类的
同步阻塞:线程在获取同步锁时,该同步锁被其他线程占用,则进入“锁池
其他阻塞sleepjoin方法,或发出I/O请求 sleep是Thread类的方法

重点:
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
新建:就是刚使用new方法,new出来的线程;
就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
image.png

1.17 final总结

用于:变量、方法、类
变量:
基本:数值不改
引用:不指向其他对象,但是引用对象的属性可以修改
类:
不能被继承
成员方法被隐式指定为final
方法:
使用原因:
1.方法锁定,防止继承类修改其含义
2.提高效率,内嵌调用

final作用,为什么局部内部类和匿名内部类只能访问局部final变量

内部类和外部类处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕被销毁。
产生问题:外部类的方法结束时,局部变量就被销毁了,但内部类对象可能还存在,内部类对象访问一个不存在的变量。
为解决这一问题,将局部变量复制了一份作为内部类的成员变量,所以要保证两个变量一样,即内部类中修改成员变量,方法中的局部变量也得跟着变,所以final。

1.18 Java中的异常处理(体系) P45

java.lang包下的Throwable类(顶级父类)的两个子类:Exception和Error

Exception

程序本身可解决,通过catch获取
受检查异常(必须处理)和不受检查异常(运行时异常)(可不处理,只是导致当前线程、方法调用失败,不影响别的线程)
不受检查异常包括:
RuntimeExeception 及其子类
NullPointException(空指针异常)
NumberFormatException(数字格式异常)
ArrayIndexOutOfBoundsException(数组越界)
ClassCastException(类型转换错误)
ArithmeticException(算术错误)

Error

程序无法处理,且无法通过catch获取。出现了程序就被迫停了
VirtualMachineError(虚拟机运行错误)
OutOfMemoryError(虚拟机内存不够错误)
NoClassDefFoundError(类定义错误)
虚拟机(JVM)选择线程终止

异常处理总结

try catch finally
finally不执行的三种情况:
1.try或finally中用System.exit(int)退出程序
2.程序所在线程死亡
3.关闭CPU
注意:try和finally中都有return语句时,返回前finally中内容仍会执行,且finally中返回值会覆盖原始返回值!

1.19 IO流 P50

基类

InputStream/Reader:字节/字符输入流
OutputStream/Writer:字节/字符输出流

有字节流为什么还要字符流?

字符流由JVM将字节转换得到。过程耗时,且不知编码类型时易乱码
所以,IO流提供一个直接操作字符的接口,方便对字符进行流操作
音频文件、图片等媒体文件——字节流
涉及字符的——字符流

BIO、NIO、AIO的区别

BIO
同步阻塞I/O模式,活动连接数不高(<单机1000)
NIO(Non-blocking I/O)
同步非阻塞I/O模型
支持面向缓冲、基于通道的I/O操作方法
应用场景:高负载、高并发的网络应用
AIO
异步非阻塞I/O

1.20 对象拷贝(其实不算拷贝)、浅拷贝和深拷贝

对象拷贝

  1. Student stu1 = new Student();
  2. Student stu2 = stu1;

最简单的复制,仅仅新定义了一个引用变量,指向了同一个对象(地址)
结果:修改stu2的基本类型或引用类型,stu1都会变

浅拷贝

需要覆盖Object类中的clone()方法。
创建一个新的对象,逐个成员依次拷贝,即这个对象有着原始对象属性值的一份精确拷贝:
属性是基本类型——拷贝其值
属性是引用类型——拷贝其内存地址
结果:
基本类型的改变不会相互影响
引用类型,由于指向了同一内存空间,改变一个,另一个也影响
image.png

  1. class Student implements Cloneable{
  2. ...
  3. @Override
  4. public Object clone() {
  5. Student stu = null;
  6. try{
  7. stu = (Student)super.clone();
  8. }catch(CloneNotSupportedException e) {
  9. e.printStackTrace();
  10. }
  11. return stu;
  12. }
  13. }
  14. main()中:
  15. Student stu2 = (Student)stu1.clone();

深拷贝

为引用类型的数据成员另辟一个独立的内存空间,实现真正内容上的拷贝
对有多层对象的,每个对象都要实现Cloneable并重写clone()方法,实现对象的串行层层拷贝
image.png

1.21 什么是反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Class类:用于操纵(反向解析)一个类的属性,方法,构造器等
获取一个Class对象

  1. String a = "java.lang.String";
  2. // 根据一类的全名字符串来获得一个类的类对象
  3. Class<?> clazz = Class.forName(a);
  4. // 获得传递过来的类的所有方法
  5. Method[] methods = clazz.getDeclaredMethods();
  6. // 获得类的所有属性
  7. Field[] declaredFields = clazz.getDeclaredFields();
  8. // 获得类的所有构造器
  9. Constructor<?>[] constructors = clazz.getDeclaredConstructors();

理解简记

反射机制指:程序在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法。
Java反射机制提供了以下功能:
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的方法

动态语言

动态语言的定义“程序运行时,允许改变程序结构或者变量类型,这种语言称为动态语言”。
Java不是动态语言,但是有一个动态相关机制:反射Reflection:可以于运行时加载、探知、使用编译 期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

反射的缺点:存在一定的性能损耗,施一公反射创建类会比直接调用类构造函数创建对象慢。根本原因:编译器没法对反射相关代码做优化。具体如下:
Java 代码是需要编译才能在虚拟机里运行的,但其实 Java 的编译期是一段不确定的操作过程。因为它可能是一个前端编译器(如 Javac)把 .java 文件编译成 .class 文件的过程;也可能是程序运行期的即时编译器(JIT 编译器,Just In Time Compiler)把字节码文件编译成机器码的过程;还可能是静态提前编译器(AOT 编译器,Ahead Of Time Compiler)直接把 .java 文件编译成本地机器码的过程。
其中即时编译器(JIT)在运行期的优化过程对于程序运行来说更重要,Java虚拟机在编译阶段的代码优化就在这里进行,由于*反射涉及动态解析的类型
,因此无法执行某些Java虚拟机优化。因此,反射操作的性能要比非反射操作慢,因此应该避免在对性能敏感的应用程序中频繁使用Java反射来创建对象。
(简述:Java的编译一般有两个过程:.java -> .class 和 .class -> 机器码,其中在第二个过程会使用JIT进行优化。
但是反射设计动态解析的类型,所以无法执行虚拟机优化)

1.22 JMM

Java内存模型简称JMM(Java Memory Model),是Java虚拟机所定义的一种抽象规范,用来屏蔽不同硬件和操作系统的内存访问差异,让java程序在各种平台下都能达到一致的内存访问效果。

内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,Java Memory Model(Java内存模型), 围绕着在并发过程中如何处理可见性、原子性、有序性这三个特性而建立的模型。
image.png
image.png
image.png

1.23 Object类的九个方法

Object类是所有类的祖宗父类,没有使用extend明显继承的类的直接父类。
Object类的方法是所有类都隐式拥有的,具体有以下9种。
image.png
hashCode、equals、toString
wait、notify、notifyall
getClass、clone
fanalize

1.24 char相关:字符’1’转成数字1

字符自动转int是转成ASCII中的数,如a转成97
可以直接用,如

  1. int[] arr = new int[128];
  2. char c = 'a';
  3. arr[c] = 3; // 这样就把arr[97]赋成了3

字符’1’转数字1:

  1. char c = '1';
  2. String sc = String.valueOf(a);
  3. int intc = Integer.parseInt(sc);

1.25 JDK1.8新特性

1.26 面向对象的四个基本特征

继承、封装、多态、抽象
image.png

1.27 泛型擦除