banner
Violet

Violet's Blog

A Web <Developer />. Code_ for Fun.
x
github
bilibili
email

Java语言基础

原文地址

Java 的特点#

  1. 面向对象。封装、多态、继承
  2. 较好的兼容性(Java 虚拟机实现跨平台,一次编译,多平台使用)
  3. 支持多线程
  4. 支持网络编程
  5. 编译与解释并从

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 关键字修饰成为常量。

字符常量和字符串常量#

  1. 形式 : 字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
  2. 含义 : 字符常量相当于一个整型值 (ASCII 值), 可以参加表达式运算;字符串常量代表一个地址值 (该字符串在内存中存放位置)。
  3. 占内存大小 : 字符常量只占 2 个字节;字符串常量占若干个字节。

静态方法为什么不能调用非静态成员#

静态方法属于类,在类加载时被创建。非静态成员属于实例,当对象被实例化时候创建。

静态方法先于非静态成员创建。

静态方法和实例方法区别#

调用方式:静态方法通过类名.方法名调用。实例方法通过对象.方法名调用。

重写和重载#

重载#

只能在同一个类中,方法名必须相同,参数类型、个数、顺序不同,方法返回值,访问修饰符可以不同。

重写#

发生在运行时

  1. 方法名,参数列表必须相同,返回类型相同
  2. 如果父类访问修饰符为 private/final/static 则子类就不能重写该方法

基本数据类型#

8 种数据类型#

  • 6 种数字类型
    • 4 种整数型:int\short\byte\long
    • 2 种浮点型:float\double
  • 1 种字符类型:char
  • 1 种布尔型:boolen

默认值及所占空间大小#

基本类型位数字节默认值取值范围
byte810-128 ~ 127
short1620-32768 ~ 32767
int3240-2147483648 ~ 2147483647
long6480L-9223372036854775808 ~ 9223372036854775807
char162'u0000'0 ~ 65535
float3240f1.4E-45 ~ 3.4028235E38
double6480d4.9E-324 ~ 1.7976931348623157E308
boolean11falsetrue、false

long类型需要在数值后加上L,否则将作为整型解析

char a = 'h'char : 单引号,String a = "hello" : 双引号。

基本类型和包装类型区别#

  • 基本类型的局部变量存放在栈中,基本类型的成员变量存放在堆中。包装类型属于对象,存放在堆中。
  • 包装类型默认值为 null。
  • 包装类型可用于泛型,基本类型不可以。

包装类型的缓存机制#

  • Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据
  • Character 创建了数值在 [0,127] 范围的缓存数据
  • Boolean 直接返回 True or False
  • Float,Double 没有缓存

如果定义新变量超出范围则会创建新的对象

拆箱与装箱#

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

为什么浮点数运算会丢失精度#

无限循环小数在计算机中保存是会被截断,导致小数精度丢失。

如何解决浮点数精度丢失#

使用BigDecimal

超过lang类型的数据如何表示#

使用BigInteger

面向对象基础#

面向对象和面向过程#

  • 面向过程:将问题解决过程拆解成一个个方法,通过一个个方法的执行解决问题。
  • 面向对象:先抽出对象,用对象执行方法的方式解决问题。

对象实体和对象引用#

对象引用:对象的实例化

通过 new 创建对象实例(对象实例存放在堆中),对象引用指向对象实例(对象引用存放在栈中)。

一个对象引用可以指向 0 或 1 个对象。

一个对象可以有 n 个引用指向它。

构造方法#

完成对象的初始化,在实例化一个对象时,后面的括号说明调用的时无参构造方法。

如果没有声明构造方法,默认会有不带参构造方法。如果声明了有参构造方法,会覆盖默认无参构造方法,需要手动再次声明无参构造方法。

构造方法必须与类名相同,不可以重写,但可重载。

面向对象的特性,封装、继承、多态#

封装#

封装是指将一个对象的属性,隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以通过一些允许被外部访问的方法来操作属性方法get(),set()来操作属性。

继承#

  • 子类拥有父类所有的属性和方法,但是无法访问父类中私有属性和方法无法访问。
  • 子类可以创建新的属性和方法,对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。

多态#

  • 编译时多态:指方法的重载
  • 运行时多态:程序中定义的对象引用所指向的具体类型在运行期间才确定

多态存在的条件:继承、重写、父类引用指向子类对象 (例如:Animal a = new Cat())

接口和抽象类#

相同点:

  1. 不能被实例化
  2. 可以包含抽象方法
  3. 可以有默认的实现方法(使用 default 关键字在接口中定义默认方法)

不同点:

  1. 单继承,多实现
  2. 接口中成员变量只能是public static final类型的,且必须有初始值。抽象类的成员变量默认 default,可以在子类中被重新定义,赋值。

深拷贝、浅拷贝、引用拷贝#

  • 浅拷贝:会在堆上创建一个新的对象,如果原对象内部属性是引用类型的话,浅拷贝会直接地址内部对象的引用地址,即拷贝对象和原对象共用同一个内部对象。
  • 深拷贝:完全复制整个对象,包括这个对象包含的内部对象。
  • 引用拷贝:两个不同的引用指向同一个对象。

image

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 对象本身进行操作,而不是生成新的对象并改变对象引用。

使用:

  1. 操作少量的数据:适用 String
  2. 单线程操作字符串缓冲区下操作大量数据:适用 StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据:适用 StringBuffer

字符串拼接不能使用+#

如果使用+拼接,实际上是通过StringBuilder调用append()方法实现,拼接之后调用toString()得到String对象。在循环体内使用+拼接时,每循环一次就会创建一个StringBuilder对象,如果手动使用StringBuilder就不会有这个问题。

String.equals () 和 Object.equals ()#

  • String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。

  • Objectequals 方法是比较的对象的内存地址。

字符串常量池#

字符串常量池作用是避免重复创建 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);
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.