Java 核心知识点查漏补缺(一)
Java 核心概念补充说明
一、封装的实际意义与示例解析
封装是面向对象三大特性之一,核心是通过访问权限控制隐藏类的内部实现细节,仅暴露必要的接口供外部交互。
以Person.java为例:
- 将
name
属性用private
修饰,限制了直接访问(如person.name = "xiaoming"
会报错) - 通过
public
修饰的getName()
和setName()
方法提供访问入口,可在方法中添加验证逻辑(如姓名长度限制) - 优势:保护数据完整性、降低代码耦合度、便于后续维护扩展
二、接口的应用场景与特性
接口是一种行为规范,用于定义类必须实现的方法,不关注具体实现。
从Flyable.java和Bird.java看接口特性:
- 抽象方法:如
fly()
,实现类必须重写(Bird
类实现了fly()
方法) - 默认方法:带
default
关键字,有默认实现,实现类可选择重写(Bird
重写了land()
方法) - 静态方法:带
static
关键字,属于接口本身,通过接口名直接调用(Flyable.info()
) - 多实现特性:类可同时实现多个接口,弥补 Java 单继承的局限性
接口降低耦合度的体现:当需要新增 "飞行" 功能的类(如Plane
)时,只需实现Flyable
接口,无需修改原有代码。
三、抽象类与接口的区别
从Animal
抽象类和Flyable
接口对比:
特性 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract class | interface |
实例化 | 不能 | 不能 |
方法类型 | 可包含抽象方法和具体方法 | JDK8 + 可包含抽象方法、默认方法、静态方法 |
继承 / 实现 | 单继承(extends ) | 多实现(implements ) |
成员变量 | 可包含各种权限的变量 | 只能是public static final 常量 |
设计意图 | 体现 "is-a" 关系(如Dog is a Animal ) | 体现 "has-a" 能力(如Bird has a Flyable 能力) |
四、内部类的特性与使用场景
以Outer.java中的成员内部类为例:
- 访问权限:内部类可直接访问外部类的所有成员(包括
private
修饰的outerVar
) - 创建方式:
- 外部类内部:直接
new Inner()
- 外部类外部:需通过外部类实例创建(
outer.new Inner()
)
- 外部类内部:直接
- 应用场景:
- 当内部类仅服务于外部类时,可隐藏实现细节
- 方便访问外部类的成员,适合编写事件监听器等场景
五、==
与equals()
的深层对比
结合Test01.java
中的示例:
- 基本数据类型:
==
比较值(如a == c
中 10 与 10.0 值相等,返回 true) - 引用数据类型:
==
比较内存地址(s1 == s2
为 false,因是两个不同对象)equals()
默认等同于==
(Object 类实现),但可重写用于内容比较(如 String 类重写后比较字符序列)
- 注意点:
- 比较字符串内容时应使用
equals()
,而非==
- 重写
equals()
时需遵循对称性、传递性等规则,通常需同时重写hashCode()
- 比较字符串内容时应使用
六、静态成员的特性与生命周期
从Test01.java的Student
类示例:
- 静态变量(
static String school
):- 属于类本身,所有对象共享同一份内存
- 一个对象修改后,所有对象访问到的都是修改后的值
- 静态方法(
static void eat()
):- 通过类名直接调用(
Student.eat()
),无需创建对象 - 不能访问非静态成员(因静态成员加载时非静态成员可能尚未初始化)
- 通过类名直接调用(
- 静态代码块:
- 类加载时执行,且只执行一次
- 用于初始化静态资源,执行顺序早于构造器
七、多态的实现与优势
在Test02.java中体现:
- 向上转型:
Animal dog = new Dog("旺财")
- 父类引用指向子类对象,自动完成
- 可调用父类声明的方法,实际执行子类重写的实现(如
dog.eat()
执行Dog
类的eat()
)
- 优势:
- 提高代码灵活性:同一方法调用可表现不同行为
- 便于扩展:新增子类(如
Cat
)无需修改使用父类的代码
instanceof
运算符:判断对象实际类型,避免转型错误(如animal instanceof Dog
)
八、权限修饰符的实际应用
根据Test01.java中的权限表:
private
:仅本类可见,用于隐藏内部状态(如Person
的name
)default
(缺省):同一包内可见,适合包内组件间协作protected
:同一包或不同包子类可见,用于继承体系中的成员共享public
:全局可见,用于对外提供的接口方法(如Person
的getName()
)
合理使用权限修饰符可降低类之间的依赖,提高代码安全性和可维护性。
整理在一起,写成word文档形式
Java 核心知识点全解析
一、数据类型与类型转换
(一)数据类型分类
Java 数据类型分为基本数据类型和引用数据类型,二者在存储方式和使用场景上有显著区别。
- 基本数据类型(8 种):直接存储数据值,占用固定内存空间。
- 整数类型:
byte
(1 字节)、short
(2 字节)、int
(4 字节)、long
(8 字节),用于存储整数数值,根据数值范围选择对应类型。 - 浮点类型:
float
(4 字节)、double
(8 字节),用于存储小数,double
精度高于float
,日常开发中更常用。 - 字符类型:
char
(2 字节),用于存储单个字符,如'a'
、'中'
。 - 布尔类型:
boolean
,仅表示true
(真)或false
(假),JVM 未明确规定其占用内存大小。
- 整数类型:
- 引用数据类型:存储对象的内存地址,而非数据本身,包括类、接口、数组三类。
(二)类型转换规则
类型转换需遵循 “范围匹配” 原则,避免数据精度丢失或运行错误。
- 自动转换:小范围类型向大范围类型转换,无需显式声明,数据不会丢失。
- 转换顺序:
byte→short→int→long→float→double
- 示例:
int a = 10; long b = a;
,a
的int
类型自动转换为long
类型。
- 转换顺序:
- 强制转换:大范围类型向小范围类型转换,需显式添加目标类型括号,可能导致数据精度丢失。
- 示例:
int c = 100; byte d = (byte)c;
,若c
值超过byte
范围(-128~127),转换后数据会异常。
- 示例:
二、运算符
运算符是用于执行数据运算的符号,Java 提供多种运算符以满足不同计算需求。
- 算术运算符:用于基本数学运算,包括
+
(加)、-
(减)、*
(乘)、/
(除)、%
(取余)、++
(自增)、--
(自减)。- 自增 / 自减分为前缀和后缀两种形式:
- 前缀:
++a
、--a
,先执行自增 / 自减,再参与其他运算。 - 后缀:
a++
、a--
,先参与其他运算,再执行自增 / 自减。
- 前缀:
- 示例:
int a = 5; int b = a++;
(b
值为 5,a
值变为 6);int c = ++a;
(a
值变为 7,c
值为 7)。
- 自增 / 自减分为前缀和后缀两种形式:
- 赋值运算符:用于给变量赋值,基础运算符为
=
,扩展运算符包括+=
、-=
、*=
、/=
等,可简化代码并自动处理类型转换。- 示例:
int a = 3; a += 2;
(等价于a = (int)(a + 2)
,a
值变为 5)。
- 示例:
- 比较运算符:用于比较两个值的关系,结果为
boolean
类型(true
或false
),包括>
(大于)、<
(小于)、>=
(大于等于)、<=
(小于等于)、==
(等于)、!=
(不等于)。- 示例:
int a = 5, b = 3; System.out.println(a > b);
(输出true
)。
- 示例:
- 逻辑运算符:用于连接布尔表达式,结果为
boolean
类型,包括&&
(短路与)、||
(短路或)、!
(非)、&
(与)、|
(或)、^
(异或)。- “短路” 特性:
&&
若左侧表达式为false
,右侧不再执行;||
若左侧表达式为true
,右侧不再执行。 - 示例:
int a = 2; System.out.println(a > 3 && ++a > 2);
(左侧为false
,a
值仍为 2)。
- “短路” 特性:
- 位运算符:直接对二进制位进行运算,包括
&
(按位与)、|
(按位或)、^
(按位异或)、~
(按位取反)、<<
(左移)、>>
(右移),常用于底层开发或高效运算。- 示例:
int a = 3; int b = a << 1;
(3
二进制为11
,左移 1 位后为110
,b
值为 6)。
- 示例:
- 三元运算符:语法为
条件表达式 ? 表达式1 : 表达式2
,根据条件表达式结果选择执行表达式 1 或表达式 2,可简化简单的分支逻辑。- 示例:
int max = (a > b) ? a : b;
(若a > b
,max
取a
值,否则取b
值)。
- 示例:
三、流程控制
流程控制用于控制代码的执行顺序,包括分支结构和循环结构两类。
(一)分支结构
根据条件判断执行不同代码块,主要有if-else
和switch
两种形式。
- if-else:适合范围性条件判断,可嵌套使用,逻辑清晰。
- 语法:
if (条件1) {// 条件1为true时执行 } else if (条件2) {// 条件1为false、条件2为true时执行 } else {// 所有条件均为false时执行 }
- 示例:
int score = 85; if (score > 90) {System.out.println("优秀"); } else if (score > 60) {System.out.println("及格"); } else {System.out.println("不及格"); }
- switch:适合等值条件判断,JDK7 及以上支持
String
类型,需注意 “穿透” 问题。- 语法:
switch (表达式) {case 值1:// 表达式等于值1时执行break; // 终止switch,避免穿透case 值2:// 表达式等于值2时执行break;default:// 表达式不等于任何case值时执行 }
- 示例:
String day = "周一"; switch (day) {case "周一":System.out.println("工作日第一天");break;case "周日":System.out.println("休息日");break;default:System.out.println("普通日期"); }
- 注意:若省略
break
,会从匹配的case
开始,依次执行后续所有代码,直至遇到break
或switch
结束,即 “switch 穿透”。
(二)循环结构
用于重复执行某段代码,主要有for
、while
、do-while
三种形式。
- for:适合已知循环次数的场景,语法简洁,控制变量作用域明确。
- 语法:
for (初始化语句; 循环条件; 更新语句) {// 循环体:条件为true时执行 }
- 示例:
for (int i = 0; i < 5; i++) {System.out.println("循环第" + (i + 1) + "次"); }
- while:适合未知循环次数的场景,先判断条件,再执行循环体。
- 语法:
while (循环条件) {// 循环体:条件为true时执行 }
- 示例:
int count = 0; while (count < 3) {System.out.println("count: " + count);count++; }
- do-while:与
while
类似,但先执行一次循环体,再判断条件,确保循环体至少执行一次。- 语法:
do {// 循环体 } while (循环条件);
- 示例:
int num = 5; do {System.out.println("num: " + num);num--; } while (num > 3);
- 循环控制关键字:
break
:立即终止当前所在的循环或switch
,跳出代码块。continue
:跳过当前循环的剩余代码,直接进入下一次循环判断。
四、类与对象
类与对象是面向对象编程的核心,类是对象的模板,对象是类的实例。
(一)类的结构
类包含属性(成员变量)、方法、构造器、代码块等组件,共同描述对象的特征和行为。
- 属性(成员变量):用于存储对象的状态信息,声明在类中、方法外,有默认初始值(如
int
默认 0,String
默认null
)。- 示例:
public class Person { String name; int age; }
,name
和age
即为Person
类的属性。
- 示例:
- 方法:用于定义对象的行为,包含方法名、参数列表、返回值类型、方法体。
- 示例:
public class Person {public void sayHello() {System.out.println("Hello!");} }
- 构造器:与类名相同,无返回值类型,用于创建对象时初始化属性,若未自定义,编译器会生成默认无参构造器。
- 示例:
public class Person {String name;// 自定义构造器public Person(String name) {this.name = name;} }
- 代码块:分为静态代码块和实例代码块,用于初始化资源。
- 静态代码块:用
static
修饰,类加载时执行,仅执行一次,优先于构造器。 - 实例代码块:无
static
修饰,创建对象时执行,每次创建对象都会执行。 - 示例:
public class Person {// 静态代码块static {System.out.println("类加载时执行");}// 实例代码块{System.out.println("创建对象时执行");} }
- 静态代码块:用
(二)访问权限修饰符
访问权限修饰符用于控制类、属性、方法的访问范围,保障代码安全性和封装性,共 4 种。
修饰符 | 同一类中 | 同一包中 | 同包子类中 | 不同包中 |
---|---|---|---|---|
public | ✔️ | ✔️ | ✔️ | ✔️ |
protected | ✔️ | ✔️ | ✔️ | ❌ |
默认(无修饰符) | ✔️ | ✔️ | ❌ | ❌ |
private | ✔️ | ❌ | ❌ | ❌ |
- 应用场景:
private
:修饰属性,仅本类可访问,通过getter/setter
对外暴露访问接口。public
:修饰对外提供的类、方法,方便外部调用。
(三)封装
封装是面向对象三大特性之一,核心是 “隐藏内部细节,暴露安全接口”,通过private
修饰属性、提供public
的getter/setter
方法实现。
- 示例(参考
Person.java
):
public class Person {// private修饰属性,隐藏内部细节private String name;private int age;// getter方法:获取属性值public String getName() {return name;}// setter方法:设置属性值,可添加验证逻辑public void setName(String name) {// 验证姓名不为空if (name != null && !name.isEmpty()) {this.name = name;}}
}
- 优势:保护数据完整性,避免非法赋值;降低代码耦合度,便于后续维护和扩展。
五、继承与多态
继承与多态是面向对象编程的重要特性,实现代码复用和灵活扩展。
(一)继承
继承通过extends
关键字实现,子类继承父类的非私有属性和方法,Java 支持单继承(一个子类只能有一个直接父类)。
- 继承的核心特性:
- 子类可直接使用父类的非私有成员,减少代码重复。
- 子类可重写父类方法,实现个性化逻辑。
- 通过
super
关键字调用父类的构造器、属性、方法。
- 示例:
// 父类
public class Animal {public void eat() {System.out.println("动物进食");}
}// 子类,继承Animal
public class Dog extends Animal {// 重写父类eat方法@Overridepublic void eat() {System.out.println("狗吃骨头");}
}
(二)抽象类
抽象类用abstract
关键字修饰,是包含抽象方法的类,不能实例化,需通过子类继承并实现抽象方法使用。
- 抽象类的特性:
- 可包含抽象方法(无方法体,用
abstract
修饰)和具体方法(有方法体)。 - 非抽象子类继承抽象类后,必须实现所有抽象方法;若子类也为抽象类,可不用实现。
- 可包含抽象方法(无方法体,用
- 示例(参考
Test02.java
):
// 抽象父类
abstract class Animal {String name;// 具体方法public void sleep() {System.out.println(name + "在睡觉");}// 抽象方法public abstract void eat();
}// 非抽象子类,实现抽象方法
class Dog extends Animal {public Dog(String name) {this.name = name;}@Overridepublic void eat() {System.out.println(name + "吃骨头");}
}
(三)多态
多态是指同一行为在不同对象上有不同表现形式,核心是 “父类引用指向子类对象”(向上转型),需满足 “继承”“方法重写”“向上转型” 三个条件。
- 多态的实现:
- 向上转型:自动转换,父类引用指向子类对象,可调用父类声明的方法,实际执行子类重写的实现。
- 示例(参考
Test02.java
):
Animal dog = new Dog("旺财"); // 向上转型 dog.eat(); // 执行Dog类的eat(),输出“旺财吃骨头” dog.sleep(); // 执行Animal类的sleep(),输出“旺财在睡觉”
- 多态的优势:
- 提高代码灵活性:同一方法调用可根据对象实际类型执行不同逻辑。
- 便于扩展:新增子类(如
Cat
)时,无需修改使用父类的代码,符合 “开闭原则”。
instanceof
运算符:用于判断对象的实际类型,避免转型错误,返回值为boolean
类型。- 示例:
Animal animal = new Dog("旺财"); System.out.println(animal instanceof Dog); // true(animal实际是Dog对象) System.out.println(animal instanceof Animal); // true(Dog是Animal子类)
六、接口
接口是一种行为规范,用interface
关键字定义,用于约束类的行为,不关注具体实现,弥补 Java 单继承的局限性。
(一)接口的结构
接口包含抽象方法、默认方法(JDK8+)、静态方法(JDK8+),所有成员默认用public
修饰(可省略)。
- 抽象方法:无方法体,实现类必须重写。
- 默认方法:用
default
修饰,有方法体,实现类可选择重写或直接使用。 - 静态方法:用
static
修饰,有方法体,属于接口本身,通过接口名直接调用,实现类不能重写。 - 示例(参考
Flyable.java
):
public interface Flyable {// 抽象方法void fly();// 默认方法default void land() {System.out.println("默认降落");}// 静态方法static void info() {System.out.println("这是飞行接口");}
}
(二)接口的实现
类通过implements
关键字实现接口,可同时实现多个接口,需重写所有接口的抽象方法。
- 示例(参考
Bird.java
和Test02.java
):
// 实现Flyable接口
class Bird implements Flyable {// 重写抽象方法fly()@Overridepublic void fly() {System.out.println("鸟儿飞翔");}// 重写默认方法land()(可选)@Overridepublic void land() {System.out.println("鸟儿降落");}
}// 测试
public class Test02 {public static void main(String[] args) {Flyable bird = new Bird();bird.fly(); // 输出“鸟儿飞翔”bird.land(); // 输出“鸟儿降落”Flyable.info(); // 输出“这是飞行接口”(调用静态方法)}
}
- 接口的优势:
- 降低耦合度:接口定义规范,实现类可独立变化,如新增
Plane
类实现Flyable
接口,无需修改原有代码。 - 支持多实现:一个类可实现多个接口,满足多种行为需求,如
class Plane implements Flyable, Runnable
。
- 降低耦合度:接口定义规范,实现类可独立变化,如新增
七、内部类
内部类是定义在另一个类(外部类)内部的类,根据位置和修饰符分为成员内部类、静态内部类、局部内部类、匿名内部类,此处重点介绍成员内部类(参考Outer.java
)。
(一)成员内部类的特性
成员内部类定义在外部类内部、方法外,无static
修饰,与外部类实例关联。
- 访问权限:可直接访问外部类的所有成员(包括
private
修饰的成员),外部类需通过内部类实例访问内部类成员。 - 创建方式:
- 外部类内部:直接
new 内部类()
创建。 - 外部类外部:需先创建外部类实例,再通过
外部类实例.new 内部类()
创建。
- 外部类内部:直接
- 示例(参考
Outer.java
):
public class Outer {private int outerVar = 10; // 外部类私有成员// 成员内部类class Inner {int innerVar = 20;public void innerMethod() {// 访问外部类私有成员System.out.println("外部类变量:" + outerVar);System.out.println("内部类变量:" + innerVar);}}// 外部类方法中使用内部类public void outerMethod() {Inner inner = new Inner(); // 外部类内部创建内部类实例inner.innerMethod();}public static void main(String[] args) {// 外部类外部创建内部类实例Outer outer = new Outer();Outer.Inner inner = outer.new Inner();inner.innerMethod(); // 输出“外部类变量:10”“内部类变量:20”}
}
(二)成员内部类的应用场景
- 当内部类仅服务于外部类,且需要频繁访问外部类成员时使用,可隐藏内部实现细节,提高代码封装性,如事件监听器、迭代器等场景。
八、关键字补充
(一)final
final
用于修饰类、方法、变量,表示 “不可变”。
- 修饰类:类不能被继承,如
final class String
,确保类的稳定性。 - 修饰方法:方法不能被重写,如父类中
final
修饰的方法,子类无法修改其逻辑。 - 修饰变量:
- 基本数据类型变量:初始化后值不可改变。
- 引用数据类型变量:初始化后内存地址不可改变,但对象内容可修改。
- 示例:
final int a = 5; // a = 6; // 报错:基本类型变量值不可变final int[] arr = {1, 2, 3}; arr[0] = 100; // 允许:修改数组内容 // arr = new int[5]; // 报错:引用地址不可变
(二)static
static
用于修饰属性、方法、代码块,属于类本身,而非对象实例。
- 静态属性:所有对象共享同一数据,通过
类名.属性名
访问,如Student.school
(参考Test01.java
)。- 示例:
class Student {static String school; // 静态属性 } Student.school = "河金"; System.out.println(Student.school); // 输出“河金”
- 静态方法:属于类,通过
类名.方法名
访问,不能访问非静态成员(非静态成员依赖对象实例),如Student.eat()
(参考Test01.java
)。- 示例:
class Student {public static void eat() {System.out.println("吃饭");} } Student.eat(); // 直接通过类名调用
- 注意:静态成员加载时机早于非静态成员,在静态方法中不能使用
this
和super
关键字。
(三)this
this
代表当前对象的引用,用于区分成员变量和局部变量、调用当前类的构造器、调用当前类的方法。
- 示例:
public class Person {private String name;public Person(String name) {this.name = name; // 区分成员变量和局部变量}
}
九、数组
数组是存储相同类型数据的容器,属于引用数据类型,长度固定(初始化后不可改变)。
(一)数组的声明与初始化
- 声明方式:两种语法等价,推荐第一种,更符合 Java 风格。
数据类型[] 数组名
,如int[] arr
。数据类型 数组名[]
,如int arr[]
。
- 初始化方式:
- 静态初始化:指定数组元素,长度由元素个数决定,如
int[] arr = {1, 2, 3};
。 - 动态初始化:指定数组长度,元素为默认值,如
int[] arr = new int[3];
(元素默认值为 0)。
- 静态初始化:指定数组元素,长度由元素个数决定,如
(二)数组的访问与遍历
- 访问:通过 “数组名 [索引]” 访问元素,索引从 0 开始,最大索引为 “数组长度 - 1”,若索引越界会抛出
ArrayIndexOutOfBoundsException
。- 示例:
int[] arr = {1, 2, 3}; System.out.println(arr[0]);
(输出 1)。
- 示例:
- 遍历:通过
for
循环或增强for
循环(for-each
)遍历数组元素。- 示例:
int[] arr = {1, 2, 3}; // 普通for循环 for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]); } // 增强for循环 for (int num : arr) {System.out.println(num); }
十、字符串比较
字符串比较是常见操作,需区分==
和equals()
的差异,避免逻辑错误。
(一)==
的作用
- 基本数据类型:比较值是否相等,如
int a = 10, b = 10; System.out.println(a == b);
(输出true
)。 - 引用数据类型:比较对象的内存地址是否相同,即是否指向同一个对象,如
String s1 = new String("abc"), s2 = new String("abc"); System.out.println(s1 == s2);
(输出false
,因s1
和s2
是两个不同对象)。
(二)equals()
的作用
equals()
是Object
类的方法,默认实现与==
一致(比较内存地址),但许多类(如String
、Integer
)重写了该方法,用于比较对象内容是否相等。
String
类的equals()
:比较字符串的字符序列是否相同,与内存地址无关。- 示例:
String s1 = new String("abc"); String s2 = new String("abc"); System.out.println(s1.equals(s2)); // 输出true(内容相同)
- 自定义类重写
equals()
:若需比较自定义对象的内容,需手动重写equals()
方法,通常需同时重写hashCode()
方法(保证 “equals 相等的对象,hashCode 一定相等”)。- 示例:
public class Person {private String name;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name);} }