跳至主要內容

基础知识

Quest大约 16 分钟基础知识基础语法

注释

Java 中注释有三种形式,分别为:

  • 单行注释:// 开始

  • 多行注释:/* 开始,以 */结束(注:Java中多行注释不能嵌套使用,实际开发使用较少)

  • 文档注释:/**开始,以*/结束

    /

数据类型

基本数据类型

Java 中有8种基本数据类型,分别是:

  • 整型:byteshortintlong

  • 浮点型:floatdouble

  • 字符型:char

  • 布尔型:boolean

基本数据类型包装类型位数字节默认值取值范围
byteByte810-128 ~ 127
shortShort1620-32768 ~ 32767
intInteger3240-2147483648 ~ 2147483647
longLong6480L-9223372036854775808 ~ 9223372036854775807
floatFloat3240.0f1.4E-45 ~ 3.4028235E38
doubleDouble6480.0d4.9E-324 ~ 1.7976931348623157E308
charCharacter162'\u0000'0 ~ 65535
booleanBoolean1-falsetrue、false

包装类型

包装类型是将基础数据类型包装为对象的机制,每种基本数据类型都有与之对应的包装类型,包装类型提供很多方法和属性,使得对基本数据类型的操作更加方便,比如值的比较、运算、转换。此外,包装类型可用于泛型,而基本类型不可以。

自动装箱与拆箱

自动装箱:基本类型自动转换为与之对应的包装类型
自动拆箱:包装类型自动转化为与之对应的基本类型
以下为自动装箱、拆箱示例:

Integer num1 = 10; // 自动装箱
int num2 = num1; // 自动拆箱

System.out.println(num1); // 输出:10
System.out.println(num2); // 输出:10

包装类型缓存机制

包装类型的缓存机制是一种优化措施,减少频繁创建和销毁包装类型对象的开销。为了节省内存和提升系统性能。以下是包装类型缓存规则:
ByteShortIntegerLong类型缓存了[-128,127]区间的值。
Character类型缓存了[0,127]区间的值。
Boolean类型缓存了两个值:TrueFalse
FloatDouble类型没有缓存机制。
由于包装类型具有缓存机制,当调用包装类中valueof()方法创建对象时和自动装箱时,如果值位于缓存范围内,将返回缓存中的对象实例,不会创建新的对象。

大数

处理超过基本数据类型表示范围的最大值或最小值的大数,可以使用BigDecimalBigInteger处理。适用于处理大数计算和精确计算的场景,如金融计算。

BigDecimal

表示任意精度的十进制数,提供了基本的算术运算(加、减、乘、除)以及其他的与小数点精度相关的方法,通常用于金额计算。以下是BigDecimal基础用法:

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("123456.789");
BigDecimal b = new BigDecimal("987654.321");

BigDecimal sum = a.add(b); //对象相加
BigDecimal difference = a.subtract(b); //对象相减
BigDecimal product = a.multiply(b); //对象相乘
BigDecimal quotient = a.divide(b, 10, BigDecimal.ROUND_HALF_UP);//对象相除
BigDecimal compare = a.compareTo(b);//返回-1表示a小于b,0表示a等于b,1表示a大于b

System.out.println(sum);
System.out.println(difference);
System.out.println(product);
System.out.println(quotient);
System.out.println(compare);
为什么要用BigDecimal做浮点数运算

浮点数float(使用 32 位:1 个符号位 + 8 个指数位 + 23 个尾数位)和double(使用 64 位: 1个符号位 + 11 个指数位 + 52 个尾数位)是基于二进制表示形式,使用有限的位数来表示无限的小数,由于位数的限制,对于部分十进制的小数,无法精确表示值,造成精度丢失问题。
使用BigDecimal时,计算和比较都是基于十进制的,不会产生精度丢失。BigDecimal提供了许多方法来执行各种精确计算,如加法、减法、乘法、除法和取余等。可以指定精度和舍入规则来满足特定的计算需求。

相关信息

注意:BigDecimal是对象类型,与基本数据类型相比,性能和内存占用方面通常会比基础数据类型更高。因此,需要精确计算比如金额计算或者对性能要求不是非常高的情况下才使用。

BigInteger

处理超过基本整数类型(超过最大整数类型long)范围的值,如果用基本数据类型处理,可能会有数值溢出风险,处理超出范围的大整数可以通过BigInteger进行处理。以下是BigInteger基础用法:

import java.math.BigInteger;

BigInteger bigNum1 = new BigInteger("736463736762328798735");
BigInteger bigNum2 = new BigInteger("232732973283278327382");

BigInteger sum = bigNum1.add(bigNum2); //对象相加
BigInteger difference = bigNum1.subtract(bigNum2); //对象相减
BigInteger product = bigNum1.multiply(bigNum2); //对象相乘
BigInteger quotient = bigNum1.divide(bigNum2); //对象相除

System.out.println(sum);
System.out.println(difference);
System.out.println(product);
System.out.println(quotient);

相关信息

注意:BigInteger 运算相对基本整数类型运算效率会更低。

变量

变量分类

  • 局部变量

在方法、构造函数或代码块内部声明的变量。局部变量只在所在的方法、构造函数或代码块中可见。局部变量在声明时不会自动初始化,必须显式地赋予初始值才能使用。

  • 成员变量

类中定义的变量。成员变量属于类的实例,即对象,每个对象都有一组成员变量副本。成员变量自动初始化为默认值,例如数值类型为0,引用类型为null,可以通过对象访问成员变量。

  • 静态变量

类中使用static修饰符声明的变量。静态变量属于类本身,不是类的实例。所有该类实例的对象共享相同静态变量副本。静态变量在类加载时初始化,并且只会初始化一次,可以通过类名直接访问静态变量。

  • 参数变量

在方法或构造函数声明中用于接收传递给方法或构造函数的值的变量,即形参。参数变量的值由调用方法或创建对象时传递的实际参数确定。

局部、成员、静态变量区别

  • 初始化:

    局部变量声明时要显式初始化;成员变量、静态变量如果没有显式初始化可以有默认值也可以在声明时初始化(final修饰的变量必须显式初始化)。

  • 作用域:

    局部变量作用域限定在声明的方法、构造函数、代码块中;成语变量和静态变量的作用域在整个类中可见。

  • 访问方式:

    局部变量只能在声明的方法、构造函数、代码块中访问;成语变量可以通过实例对象访问;静态变量可以通过类名访问。

  • 存储位置:

    局部变量存储位于栈内存中;成员变量属于实例对象,因而存储位于堆内存的对象中;静态变量存储在堆内存的方法区中的静态存储区域。

  • 生命周期:

    局部变量的生命周期随着在声明的方法、构造函数、代码块中调用执行而存在;成员变量的生命周期随对象的创建而存在;静态变量随着类的加载而存在,类被卸载时,静态变量被销毁,并释放内存空间。

参数传递问题

基本数据类型作为方法参数传递时,会将值拷贝后传递,引用类型作为方法参数传递时,会将引用类型地址拷贝后传递,本质上是将对象的地址以值的方式传递到形参中(Java 的参数是以值传递的形式传入方法中,而不是引用传递),指向同一个对象。对引用类型变量的操作会影响到同一对象的其他引用。

方法

方法是封装了一段特定功能代码块,用于执行特定的任务。方法可以接收参数、执行操作、可能返回一个执行结果。方法由修饰符、返回类型、方法名、参数列表、异常声明、方法体构成。方法可以分为以下几种类型:

  • 实例方法:

    实例方法属于对象的方法,通过实例化对象进行调用,实例方法可以访问和操作对象的成员变量,可以调用其他实例方法和静态方法。

  • 静态方法:

    静态方法属于类的方法,通过类名直接调用,不用实例化对象。静态方法不能直接访问实例变量,只能访问静态变量。

  • 构造方法:

    构造方法的名称与类名称相同,无返回类型,用于创建和初始化对象。在使用new创建对象时被隐式调用,类中如果没有显示定义构造方法,Java会提供一个默认无参的构造方法。

  • 抽象方法:

    使用abstract关键字定义的方法为抽象方法,是没有具体实现体只有方法声明的方法,必须是在抽象类中或接口中定义,需要由子类实现,抽象方法主要定义接口和抽象类的设计规范。

  • 重载方法:

    同一个类中有多个方法名称相同,参数列表不同的方法。重载方法可以有不同的返回类型。

  • 重写方法:

    重写方法指在子类中重新定义从父类继承而来的方法。重写方法必须具有相同的方法名称、参数列表、返回值类型。可以通过相同的访问修饰符或更宽松的访问修饰符修饰。

修饰符

访问修饰符

Java中,有四种访问修饰符来控制类、接口、方法、变量的访问权限。

  • public

修饰类和接口:可以从任何地方访问,被任何类继承。
修饰方法和变量:可以从任何地方访问,被任何子类继承并访问。

  • protected

修饰类和接口:只能在同一包内或子类中访问,不能被其他类继承(接口中的方法默认是public,因此可以被其他类实现)。
修饰方法和变量:只能在同一包内或子类中访问,可以被继承并在子类中访问,无论子类是否在同一包内。

  • private

修饰类和接口:只能在定义它们的类内部访问,不能被继承。
修饰方法和变量:只能在定义它们的类内部访问,无法被继承且在子类中访问。

  • default(默认,什么也不写)

修饰类和接口:只能在同一包内访问且同一包内的类继承。
修饰方法和变量:只能在同一包内访问,可以被继承并在子类中访问且子类在同一包内时才能访问。

非访问修饰符

  • final:修饰变量表示不可更改的常量值、修饰方法表示方法不能重写、修饰类表示不可继承的类
  • abstract:修饰方法表示抽象方法,修饰类代表抽象类。
  • static:表示静态变量或静态方法,属于类不是对象实例。
  • volatile:表示变量在多个线程之间可见。
  • synchronized:用于多线程同步,限制同时只有一个线程访问方法或代码块。
  • transient: 表示变量不会被序列化(持久化)。
  • native: 表示方法的实现非Java代码提供。

运算符

  • 算术运算符: 执行数学运算,常见的包含+加法、-减法、*乘法、/除法、%取余、++自增、--自减等。

  • **赋值运算符: **用于将一个值赋给变量。常见的包含=赋值、+=加法赋值、-=减法赋值、*=乘法赋值、/=除法赋值、%=取余赋值等。

  • 比较运算符: 用于比较两个值得大小关系,并返回布尔值truefalse,常见的包含==等于、!=不等于、>大于、<小于、>=大于等于、<=小于等于等。

  • 逻辑运算符: 用于处理布尔值,进行逻辑运算。常见的包含&&逻辑与、||逻辑或、!逻辑非。

  • 位运算符: 对二进制进行操作。常见的包含&按位于、|按位或、^按位异或、~按位取反、 <<左移、>>右移

  • 三元运算符: 根据条件选择性地赋值给变量,使用?:符号,格式:variable = (condition) ? expression1 : expression2;

  • instanceof运算符: 用于判断对象是否属于特定的类及子类。

控制结构

  • 条件语句

if语句:根据条件判断执行不同的代码块。
if-else语句:条件成立时执行一个代码块,不成立时执行另一个代码块。
else-if语句:多个条件之间进行判断,选取满足条件的第一个分支执行对应代码块。
switch case语句:根据表达式的值,判断与各个case的常量值匹配的代码块。

String abbreviation = '粤';
 
      switch(abbreviation) //只能用于 byte、short、int、char、String
      {
         case "沪" : //case 分支必须是一个常量表达式,编译时就能确定的值,不允许变量
            System.out.println("上海"); 
            break; //防止执行流下穿到下一个 case 分支
         case "粤" :
            System.out.println("广东");
            break;
         case "鲁" :
            System.out.println("山东");
            break;
         case "豫" :
            System.out.println("河南");
            break;
         default : //没有匹配到任何 case 分支时执行
            System.out.println("未知区域");
      }
      System.out.println("区域是 " + abbreviation);
   }
  • 循环结构

for循环:根据条件和迭代器,重复执行一段代码块
foreach循环:增强for循环,用于遍历数组或集合中的元素。
while循环:只要布尔表达式为true,循环会一直执行下去。
do-while循环:先执行一次循环中的代码,然后根据条件重复执行。

  • 分支控制语句

continue语句:跳过当前循环,继续执行下一次循环。
break语句:跳出当前循环继续执行循环后面的代码、跳出switch语句。
return语句:方法中使用return时,将方法的执行结果返回给调用且终止当前方法执行。

  • 异常处理语句

try-catch语句:用于处理可能会发生异常的代码块
finally语句:

String

String类保证字符串的不可变性

  1. String类使用privatefinal修饰字符数组存储字符串,外部无法访问和修改字符数组且String不提供修改字符串的方法。
  2. String类是使用final修饰的类,无法被子类继承,从而避免子类中修改字符数组。
  3. 对字符串进行拼接、替换操作时,Strin g类不会在原始字符串上直接进行修改,而是创建一个新的字符串对象,并将修改后的内容复制到新的字符串中。
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    }

String类字符串不变的优点

  1. 线程安全String类的不可变性具备线程安全,可以在多线程环境共享和重用字符串。
  2. 缓存和重用:当某个String值对象已经创建过了,可以直接从字符串常量池中引用,可以提高性能和减少内存的开销。
image.png
image.png
  1. 安全哈希值String类的不可变性同时也保证了字符串的哈希值不变,确保哈希表的正确性,如果字符串是可变的,修改字符串时同时哈希值也会改变,会导致无法正确在哈希表中查找。

String、StringBuffer、StringBuilder区别

StringStringBufferStringBuilder都是用于处理字符串的类,具备以下区别:

  1. 可变性

String类是不可变的,创建后不能进行修改,每次修改字符串都会创建新的字符串对象,原始字符串并不修改。
StringBufferStringBuilder类是可变的,可以对字符串进行修改,提供添加、插入、删除和修改字符串的方法。

  1. 线程安全

String类由于不可变性,保证了多个线程之间不会修改同一个字符串对象,即线程安全。
StringBuilder类不是线程安全的,它的方法不是同步的,适用于单线程环境。
StringBuffer类是线程安全的,使用Synchronized关键字使操作同步,适用于多线程环境。

  1. 性能

String类由于不可变性,每次对字符串进行修改都会创建新的字符串对象,增加额外的内存开销。
StringBuilder类进行字符串操作时,修改的是原始字符串,不会新创建对象,适用于在单线程环境下对字符串操作。
StringBuffer类与StringBuilder类类似,适用于多线程环境对字符串操作。

String类 intern() 方法

intern()方法作用是将字符串对象添加到字符串常量池中,如果池中已存在则直接返回该对象的引用,如果不存在则在常量池中创建新的字符串对象并返回。使用intern()可以有效减少内存消耗,处理大量字符串对象时,使用字符串常量池,可以共享相同值的字符串对象,节省内存空间,提高字符串比较效率。

String str1 = new String("hello");
// 此时str2指向字符串常量池中的"hello"对象
String str2 = str1.intern();
String str3 = "hello";
System.out.println(str1 == str2); // false
System.out.println(str2 == str3); // true

Object

常用方法
Object类是所有类的根类。所有其他类都直接或间接继承Object类。Object类定义了一些通用方法,这些方法可以在任何对象调用。以下是Object类一些重要的方法:

    /**
     * 返回当前对象的运行时类对象,获取对象的类信息
     */
    public final native Class<?> getClass();
    /**
     * 返回当前对象的hash值,hash值是一个整数,用于快速确定对象的存储位置
     */
    public native int hashCode();
    /**
     * 判断当前对象与比较对象是否相等,默认比较对象的引用,可以子类重写方法修改比较方式
     */
    public boolean equals(Object obj) 
    /**
     * 创建并返回当前对象的一个副本,默认浅拷贝,通过子类中实现Cloneable接口重写方法实现深拷贝
     */
    protected native Object clone() throws CloneNotSupportedException;
    /**
     * 返回当前对象的字符串表示,默认返回对象的类名和hash值的16进制表示
     */
    public String toString() 
    /**
     * 唤醒在当前对象调用wait()方法进入等待的一个线程,多个只会唤醒一个
     */
    public final native void notify();
    /**
     * 唤醒在当前对象调用wait()方法进入等待的所有线程
     */
    public final native void notifyAll();
    /**
     * 暂停当前线程的执行,timeout为等待时间
     */
    public final native void wait(long timeout) throws InterruptedException;
    /**
     * 与上同,nanos表示额外的纳秒部分的等待时间。范围是 0 到 999,999
     */
    public final void wait(long timeout, int nanos) throws InterruptedException
    /**
     * 跟上两个wait方法一样,不过该方法没有超时,会一直等待
     */
    public final void wait() throws InterruptedException 
    /**
     * 在垃圾回收器回收对象时执行该方法
     */
    protected void finalize() throws Throwable