Java 的特点#
- 面向对象。封装、多态、继承
- 较好的兼容性(Java 虚拟机实现跨平台,一次编译,多平台使用)
- 支持多线程
- 支持网络编程
- 编译与解释并从
Write Once, Run Anywhere
JVM#
Java 虚拟机,是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现,目的是使用相同的字节码,得到相同的运行结果,是一次编译,处处运行的体现。
JRE 和 JDK#
JDK(Java Development Kit),是全功能的 Java SDK。除了包含 JRE 之外,还有编译器 javac,工具 javadoc,jdb。
JRE 是 Java 程序运行时的环境,是运行已编译 Java 程序所需内容的集合。包含 JVM,Java 类库,Java 命令和一些基础构件。正常情况下只需要运行 Java 程序使用 JRE 足够,如果需要开发或者编译 Java 程序则必须使用 JDK。
如果使用 JSP 部署 WEB 程序,则需要 JDK。因为应用程序服务器会将 JSP 转换为 Java Servlet,需要 JDK 来编译 Servlet。
字节码#
JVM 可以理解的代码叫做字节码,扩展名为.class 的文件。
字节码解决了传统解释型语言执行效率低的问题,同时保留了解释型语言可以值的提点。
编译与解释并存#
编译型:通过编译器一次性将所有代码翻译成机器码。执行速度快但开发效率低。如 C、C++、Go、Rust。
解释型:通过解释器一句一句将代码解释成机器码再执行。开发快但执行速度满。如 Python,PHP,JS。
Java 语言即具有编译型语言的特征,也具有解释型语言的特征。Java 程序需要经过先编译,再解释两个过程。
通过编译生成字节码.class 文件,再有 Java 解释器解释执行。
基础语法#
continue、break 和 return#
continue:跳出当前这一次循环,继续执行下一次循环。
break:跳出整个循环体,继续执行循环体下面的代码。
return:天出当前方法,结束该方法的执行。
成员变量和局部变量#
- 语法形式:成员变量属于类,局部变量属于方法。成员变量可以被 public、private、static 修饰符修饰,局部变量不行。两者都能被 final 修饰。
- 存储方式:如果成员变量被 static 修饰,这个成员变量属于类,如果没有被 static 修饰,成员变量属于实例。对象存储与堆中,局部变量存储在栈中。
- 生存时间;成员变量是对象的一部分,随着对象的创建而创建。局部变量随着方法调用生成,随方法效用结束而结束。
- 默认值:成员变量如果没有被赋初始值,会自动以类型的默认值赋值(例如 boolen 默认 false,int 默认 0,string 默认 null)。局部变量不会自动赋值。
静态变量#
静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。
通常情况下,静态变量会被 final
关键字修饰成为常量。
字符常量和字符串常量#
- 形式 : 字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
- 含义 : 字符常量相当于一个整型值 (ASCII 值), 可以参加表达式运算;字符串常量代表一个地址值 (该字符串在内存中存放位置)。
- 占内存大小 : 字符常量只占 2 个字节;字符串常量占若干个字节。
静态方法为什么不能调用非静态成员#
静态方法属于类,在类加载时被创建。非静态成员属于实例,当对象被实例化时候创建。
静态方法先于非静态成员创建。
静态方法和实例方法区别#
调用方式:静态方法通过类名.方法名
调用。实例方法通过对象.方法名
调用。
重写和重载#
重载#
只能在同一个类中,方法名必须相同,参数类型、个数、顺序不同,方法返回值,访问修饰符可以不同。
重写#
发生在运行时
- 方法名,参数列表必须相同,返回类型相同
- 如果父类访问修饰符为
private/final/static
则子类就不能重写该方法
基本数据类型#
8 种数据类型#
- 6 种数字类型
- 4 种整数型:
int\short\byte\long
- 2 种浮点型:
float\double
- 4 种整数型:
- 1 种字符类型:
char
- 1 种布尔型:
boolen
默认值及所占空间大小#
基本类型 | 位数 | 字节 | 默认值 | 取值范围 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 |
short | 16 | 2 | 0 | -32768 ~ 32767 |
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 |
char | 16 | 2 | 'u0000' | 0 ~ 65535 |
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
boolean | 1 | 1 | false | true、false |
long
类型需要在数值后加上L
,否则将作为整型解析
char a = 'h'
char : 单引号,String a = "hello"
: 双引号。
基本类型和包装类型区别#
- 基本类型的局部变量存放在栈中,基本类型的成员变量存放在堆中。包装类型属于对象,存放在堆中。
- 包装类型默认值为 null。
- 包装类型可用于泛型,基本类型不可以。
包装类型的缓存机制#
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据Character
创建了数值在 [0,127] 范围的缓存数据Boolean
直接返回True
orFalse
Float
,Double
没有缓存
如果定义新变量超出范围则会创建新的对象
拆箱与装箱#
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
为什么浮点数运算会丢失精度#
无限循环小数在计算机中保存是会被截断,导致小数精度丢失。
如何解决浮点数精度丢失#
使用BigDecimal
超过lang
类型的数据如何表示#
使用BigInteger
面向对象基础#
面向对象和面向过程#
- 面向过程:将问题解决过程拆解成一个个方法,通过一个个方法的执行解决问题。
- 面向对象:先抽出对象,用对象执行方法的方式解决问题。
对象实体和对象引用#
对象引用:对象的实例化
通过 new 创建对象实例(对象实例存放在堆中),对象引用指向对象实例(对象引用存放在栈中)。
一个对象引用可以指向 0 或 1 个对象。
一个对象可以有 n 个引用指向它。
构造方法#
完成对象的初始化,在实例化一个对象时,后面的括号说明调用的时无参构造方法。
如果没有声明构造方法,默认会有不带参构造方法。如果声明了有参构造方法,会覆盖默认无参构造方法,需要手动再次声明无参构造方法。
构造方法必须与类名相同,不可以重写,但可重载。
面向对象的特性,封装、继承、多态#
封装#
封装是指将一个对象的属性,隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以通过一些允许被外部访问的方法来操作属性方法get(),set()
来操作属性。
继承#
- 子类拥有父类所有的属性和方法,但是无法访问父类中私有属性和方法无法访问。
- 子类可以创建新的属性和方法,对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
多态#
- 编译时多态:指方法的重载
- 运行时多态:程序中定义的对象引用所指向的具体类型在运行期间才确定
多态存在的条件:继承、重写、父类引用指向子类对象 (例如:Animal a = new Cat()
)
接口和抽象类#
相同点:
- 不能被实例化
- 可以包含抽象方法
- 可以有默认的实现方法(使用 default 关键字在接口中定义默认方法)
不同点:
- 单继承,多实现
- 接口中成员变量只能是
public static final
类型的,且必须有初始值。抽象类的成员变量默认 default,可以在子类中被重新定义,赋值。
深拷贝、浅拷贝、引用拷贝#
- 浅拷贝:会在堆上创建一个新的对象,如果原对象内部属性是引用类型的话,浅拷贝会直接地址内部对象的引用地址,即拷贝对象和原对象共用同一个内部对象。
- 深拷贝:完全复制整个对象,包括这个对象包含的内部对象。
- 引用拷贝:两个不同的引用指向同一个对象。
String Object#
== 和 equals () 区别#
- == 判断引用是否指向堆内存的同一块地址
- equals 比较堆中的内容是否相同,不比较地址
重写 hashCode () 必须重写 equals ()#
因为不同对象的 hashCode 可能相同,但是 hashCode 不同的对象一定不相同
如果只重写一个,可能会出现 equals 相同但是 hashCode 不相同
使用 HashSet 时,先判断 HashCode 是否相同,如果相同再使用 equals 方法判断是否相同。
String、StringBuffer、StringBuilder#
StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全。StringBuilder
没有对方法进行加同步锁,所以线程不安全。String
不可变(保存字符串的数组被final
修饰且为私有的),每次对String
类型进行改变的时候,都会生成一个新的String
对象,然后将指针指向新的String
对象。StringBuffer
每次都会对StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。
使用:
- 操作少量的数据:适用
String
- 单线程操作字符串缓冲区下操作大量数据:适用
StringBuilder
- 多线程操作字符串缓冲区下操作大量数据:适用
StringBuffer
字符串拼接不能使用+
#
如果使用+
拼接,实际上是通过StringBuilder
调用append()
方法实现,拼接之后调用toString()
得到String
对象。在循环体内使用+
拼接时,每循环一次就会创建一个StringBuilder
对象,如果手动使用StringBuilder
就不会有这个问题。
String.equals () 和 Object.equals ()#
-
String
中的equals
方法是被重写过的,比较的是 String 字符串的值是否相等。 -
Object
的equals
方法是比较的对象的内存地址。
字符串常量池#
字符串常量池作用是避免重复创建 String 类导致内存消耗专门开辟的区域。
当创建一个 String 类型对象时,会将对象的内容保存到堆中,将对象的引用保存到常量池。再次创建相同的 String 对象时,如果堆中已经有相同的对象,则直接返回常量池中保存的引用。
String s1 = new String ("abc") 会创建几个字符串对象#
如果字符串 "abc" 之前已经创建过了,则会创建 1 个字符串对象。如果字符串 "abc" 之前未创建,则会创建 2 个字符串对象
intern 方法#
intern 方法将指定的字符串对象的引用保存在字符串常量池
- 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
- 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。
String 类型的变量和常量做 “+” 运算#
- 如果是编译期可以确定值的字符串,JVM 会将拼接后的字符串引用放入常量池。
例如:String str3 = "str" + "ing"
会优化成 String str3 = "string";
- 如果是编译期间无法确定的字符串,
StringBuilder
调用append()
之后在调用toString()
得到一个String
异常#
Exception 和 Error#
Java 中主要由两种异常 Exception 和 Error。
- Exception: 程序本身可以处理的异常,可以通过 catch 捕获。Exception 有分为 Checked Exception受检查异常,必须处理 和 Unchecked Exception 不受检查异常,可以不处理
- Error:程序无法处理的异常,出现 Error 会导致线程终止。例如 Java 虚拟机运行异常,内存溢出,堆栈溢出等。
Checked Exception 和 Unchecked Exception#
- Checked Exception 受检查异常,在编译过程中,如果检查出受检查异常没有被catch或者throws,编译将无法通过。
- 除了 RuntimeException 及其子类外,其他 Exception 都属于受检查异常。
- Unchecked Exception 不受检查异常,在编译过程中可以不处理。
- RuntimeException 及其子类都是不受检查异常,常见的有:空指针、数组越界等
Throwable 类常用方法#
getMessage()
返回异常的简要描述toString()
返回异常详细信息getLocalizedMessage()
返回异常对象本地化信息printStackTrace()
在控制台打印出 Throwable 对象封装的异常信息
try-catch-finally 使用#
- try:用户捕获异常,后必须要接 catch 或者 finally 中一个
- catch:处理 try 捕获到的异常
- finally:无论是否捕获到或处理异常,finally 都会被执行。如果 try 或者 catch 中有 return,fianlly 会在方法返回之前执行。
fianlly 是否一定会执行#
不一定,如果 fianlly 之前 JVM 终止运行,fianlly 就不会执行,后者程序所在的线程死亡,或者 cpu 关闭
泛型#
泛型使用的方式#
- 泛型类
- 泛型接口
- 泛型方法
反射#
什么是反射#
在程序运行过程中,动态获取一个对象所属的类,获取类的成员变量和方法,调用类的属性和方法
什么地方用到了反射#
spring 通过注解 @Component 获取到类的信息,属性,参数等。
如何使用反射获取一个类#
-
通过
类名.class
Class alunbarClass = TargetObject.class;
-
通过
Class.forName(“类名”)
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
-
通过方法
Object.getClass()
TargetObject o = new TargetObject(); Class alunbarClass2 = o.getClass();
-
通过类加载器
xxxClassLoader.loadClass()
传入类路径获取:ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
注解#
什么是注解#
可以修饰类、方法、变量,用于提供信息给程序编译或运行期间使用
注解解析的方式#
- 编译期间扫描
- 运行期间通过反射处理
引用类型#
通过引用类型来判断对象是否可以被垃圾回收器回收♻️
强引用#
通过new
关键字创建的对象,是强引用,强引用的对象不会被 GC 回收。
软引用#
软引用对象,会在内存不足时被回收。
通过SoftReference
关键字可以创建软引用对象
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
弱引用#
弱引用对象会被直接回收,创建的弱引用对象只能存活到下次 GC 发生之前。
可以通过WeakReference
关键字创建弱引用对象
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
虚引用#
不能通过虚引用获取一个对象,虚引用不会对对象产生任何影响。
虚引用的作用是:虚引用的对象被 GC 回收时可以收到一个通知。
可以通过PhantomReference
关键字创建需引用对象。
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);