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);
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。