static
static
是Java中的一个重要关键字,用于修饰类的成员(变量、方法、代码块和嵌套类),表示这些成员属于类本身,而不是类的实例。
1. static变量(类变量)
- 用
static
修饰的变量叫做静态变量或类变量 - 属于类,而不是某个特定对象
- 所有实例共享一个静态变量
- 在类加载时初始化
java
public class Counter {
static int count = 0; // 静态变量
Counter() {
count++;
}
}
class Test {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println(Counter.count);//3
}
}
2. static方法(类方法)
- 用
static
修饰的方法为静态方法 - 可以直接通过类名调用,无需创建对象
- 静态方法中只能直接访问静态成员,不能直接访问实例成员(因为实例成员需要对象存在)
- 不能使用
this
和super
关键字
csharp
public class MathUtils {
static int add(int a, int b) {
return a + b;
}
}
class Main {
public static void main(String[] args) {
int sum = MathUtils.add(5, 3); // 直接通过类名调用
System.out.println("Sum: " + sum);
}
}
3. static代码块
- 用于初始化静态变量
- 在类加载时执行,只执行一次
- 可以有多个静态代码块,按顺序执行
ini
class Database {
static String url;
static String username;
static String password;
static {
// 静态代码块
url = "jdbc:mysql://localhost:3306/mydb";
username = "admin";
password = "password";
}
}
4. static嵌套类
- 用static修饰的嵌套类称为静态嵌套类
- 与外部类的实例无关,可以直接创建
- 只能访问外部类的静态成员
csharp
class Outer {
static int x = 10;
static class Inner {
void display() {
System.out.println("x = " + x);
}
}
}
class Main1 {
public static void main(String[] args) {
// 使用
Outer.Inner inner = new Outer.Inner();
inner.display();
}
}
5. 使用场景
- 当某个成员需要被所有实例共享时(如计数器)
- 工具类方法(如Math类中的方法)
- 常量定义(通常final一起使用)
- 主方法(main方法必须是static的)
注意事项
- 静态方法不能被重写为非静态方法(反之亦然)
- 静态方法不能被标记为abstract
- 静态成员在类加载时初始化,早于对象的创建
- 过度使用static可能导致代码难以测试和维护
static关键字是Java中实现类级别共享和工具方法的重要机制,合理使用可以提高程序效率和代码组织性.
继承
继承是面向对象编程的三大特性之一(封装、继承、多态),它允许一个类(子类)继承另一个类(父类)的属性和方法。
1. 继承的基本语法
scala
class 父类 {
// 父类的成员变量和方法
}
class 子类 extends 父类 {
// 子类特有的成员变量和方法
}
2. 继承的特点
- 代码复用:子类可以直接使用父类的非私有成员
- 扩展性:子类可以添加自己的新成员
- 单继承:Java只支持单继承(一个子类只有一个直接父类)
- 多层继承:可以形成继承层次结构(A->B->C)
- 构造方法不能继承:子类不能继承父类的构造方法,但可以通过
super()
调用
3. 继承示例
typescript
// 父类
class Animal {
String name;
public void eat() {
System.out.println(name + "正在吃东西");
}
}
// 子类
class Dog extends Animal {
public void bark() {
System.out.println(name + "正在汪汪叫");
}
}
// 使用
public class Main2 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "大黄";
dog.eat(); // 继承自Animal的方法
dog.bark(); // Dog自己的方法
}
}
4. 方法重写(Override)
- 子类可以重新定义父类中已有的方法
- 必须保持方法签名相同(方法名、参数列表)
- 访问权限不能比父类更严格
- 返回类型可以是父类方法返回类型的子类(协变返回类型)
- 可以使用@Override注解明确表示这是重写方法
scala
// 父类
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵叫");
}
}
5.super关键字
- 用于访问父类的成员(变量、方法、构造器)
super()
调用父类的构造方法(必须放在子类构造方法的第一行)super.成员
访问父类成员
6. 构造方法的继承
- 在类构造方法默认调用父类的无参构造
- 如果父类中没有无参构造,必须使用super(参数)显式调用
- super()和this()不能同时出现在一个构造方法中
scala
class Person {
String name;
Person(String name) {
this.name = name;
}
}
class Student extends Person {
int grade;
Student(String name, int grade) {
super(name); // 必须调用父类构造方法
this.grade = grade;
}
}
7. final与继承
- final class:不能被继承
- final method:不能被重写
- final variable:常量,值不能被修改
arduino
final class FinalClass { // 不能被继承
final void finalMethod() { // 不能被子类重写
final int MAX_VALUE = 100; // 常量
}
}
8. 继承与访问控制
- private成员:子类不能直接访问
- protected成员:子类可以访问(即使不在同一个包下)
- 默认(包私有):同包子类可以访问
- public成员:所有子类都可以访问
9. Object类
- Java中所有类的根父类
- 常用方法:
- toString():返回对象字符串表示
- equals():比较对象内容
- hashCode():返回对象的哈希码值
- getClass():获取对象的运行时类
10. 继承的应用场景
- is-a关系(狗是动物)
- 需要扩展或修改现有类功能
- 实现多态的基础
- 框架设计中常用的扩展机制
注意事项
- 慎用继承,优先考虑组合而非继承
- 避免过深的继承层次(一般不超过3层)
- 父类修改可能影响所有子类
- 子类不应该改变父类方法的预期行为(里氏替换原则)
继承是Java中强大的代码复用机制,但需要合理使用以避免设计上的问题.
多态
多态是面向对象编程的三大特性之一,它允许不同类的对象对同一消息做出不同的响应。多态提高了代码的灵活性和可扩展性。
1. 多态的基本概念
多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在Java中,多态主要体现在以下两个方面:
- 编译时多态:方法重载
- 运行时多态:方法重写
2. 多态的实现条件
实现多态需要满足三个条件:
- 继承关系:存在继承关系的类
- 方法重写:子类重写父类的方法
- 向上转型:父类引用指向子类对象
3. 多态的实现方式
方法重载-编译时多态
csharp
public class Calculator {
// 方法重载:同名方法,不同参数
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
}
class Test01 {
public static void main(String[] args) {
// 使用
Calculator calc = new Calculator();
System.out.println(calc.add(1, 2)); // 调用int add(int, int)
System.out.println(calc.add(1.5, 2.5)); // 调用double add(double, double)
System.out.println(calc.add(1, 2, 3)); // 调用int add(int, int, int)
}
}
方法重写-运行时多态
scala
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪叫");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵叫");
}
}
// 使用多态
public class Main {
public static void main(String[] args) {
Animal myAnimal; // 父类引用
myAnimal = new Dog(); // 向上转型
myAnimal.makeSound(); // 输出"汪汪叫"
myAnimal = new Cat(); // 向上转型
myAnimal.makeSound(); // 输出"喵喵叫"
}
}
4. 向上转型和向下转型
向上转型
- 将子类对象赋值给父类引用
- 自动进行,不需要强制转换
- 只能访问父类中声明的方法和属性
向下转型
- 将父类引用转为子类引用
- 需要强制类型转换
- 必须先向上转型才能向下转型
- 使用insatnceof进行类型检查更安全
ini
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog myDog = (Dog) animal; // 向下转型
myDog.bark(); // 可以调用Dog特有的方法
}
5.多态的应用场景
方法参数多态
csharp
public class Zoo {
public void animalSound(Animal animal) {
animal.makeSound(); // 根据实际传入的对象类型调用相应方法
}
}
class Animal {
public void makeSound() {
System.out.println("动物叫");
}
}
class Cat extends Animal {
public void makeSound() {
System.out.println("喵喵叫");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("汪汪叫");
}
}
class Main {
public static void main(String[] args) {
Zoo zoo = new Zoo();
zoo.animalSound(new Dog()); // 输出"汪汪叫"
zoo.animalSound(new Cat()); // 输出"喵喵叫"
}
}
返回类型多态
scala
class AnimalFactory {
public Animal1 getAnimal(String type) {
if ("dog".equalsIgnoreCase(type)) {
return new Dog1();
} else if ("cat".equalsIgnoreCase(type)) {
return new Cat1();
}
return null;
}
}
class Dog1 extends Animal1 {
}
class Cat1 extends Animal1 {
}
class Animal1 {
}
class Main01 {
public static void main(String[] args) {
AnimalFactory factory = new AnimalFactory();
Animal1 dog = factory.getAnimal("dog");
Animal1 cat = factory.getAnimal("cat");
}
}
集合中的多态
csharp
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
for (Animal animal : animals) {
animal.makeSound(); // 多态调用
}
6. 多态的优点和缺点
优点:
- 提高代码的可扩展性:新增的子类不影响现有的代码
- 提高代码的灵活性:同一方法处理不同的子类对象
- 提高代码的可维护性:减少重复代码
- 接口与实现分离:使用者只需要关注父类接口
缺点:
不能调用子类特有的功能
7. 多态的实现原理
Java通过动态绑定(后期绑定)实现运行时多态:
- 编译时:检查方法是否在父类中存在
- 运行时:JVM根据实际对象类型调用相应的方法
- 虚方法表:存储实际的调用的方法地址
8.注意事项
- 只有实例方法有多态,静态方法还有字段没有多态
- 私有方法不能被重写,因此没有多态
- 构造方法不能被重写,也没有多态
- final方法不能被重写,没有多态
- 访问权限:重写方法不能比被重写方法更严格
9. 多态调用成员的特点
- 变量调用:编译看左边,运行还看左边
- 方法调用:编译看左边,运行看右边
10. 综合示例
java
// 父类
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public double calculateBonus() {
return salary * 0.1; // 默认10%奖金
}
public String getDetails() {
return "Name: " + name + ", Salary: " + salary;
}
}
// 子类1
class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
@Override
public double calculateBonus() {
return super.calculateBonus() + bonus; // 经理有额外奖金
}
@Override
public String getDetails() {
return super.getDetails() + ", Bonus: " + bonus;
}
}
// 子类2
class Developer extends Employee {
private int overtimeHours;
public Developer(String name, double salary, int overtimeHours) {
super(name, salary);
this.overtimeHours = overtimeHours;
}
@Override
public double calculateBonus() {
return overtimeHours * 100; // 开发者按加班小时计算奖金
}
}
// 使用多态
public class Company {
public static void main(String[] args) {
Employee[] employees = {new Manager("张经理", 20000, 5000), new Developer("李开发", 15000, 20), new Employee("王普通", 10000)};
for (Employee emp : employees) {
System.out.println(emp.getDetails());
System.out.println("奖金: " + emp.calculateBonus());
System.out.println("------------");
}
}
}
多态是Java面向对象编程中非常强大的特性,合理使用多态可以大大提高代码的质量和可维护性。理解多态的关键在于掌握"一个接口,多种实现"的思想
包、final、权限修饰符、代码块
包
1. 包的作用
包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。
2. 包名的书写规则
包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。
3. 什么时候需要导包?什么时候不需要导包?
- 使用同一包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包
- 其他情况需要导包
- 如果使用两个包中的同类名时,需要用全类名(包名+类名)
final关键字
final
是Java中一个重要的关键字,用于表示"不可改变的"。它可以修饰类、方法和变量,具有不同的含义和作用
修饰目标 | 继承相关特性 |
---|---|
final类 | 不能被继承 |
final方法 | 不能被子类重写 |
final变量 | 与继承无关,表示常量 |
1. final变量
1.1 基本类型final变量:
final
修饰的变量表示常量,一旦被赋值就不能再修改
ini
final int MAX_VALUE = 100;
// MAX_VALUE = 200; // 编译错误,不能重新赋值
1.2 引用类型final变量
ini
final List<String> names = new ArrayList<>();
// names = new ArrayList<>(); // 编译错误,不能重新赋值
names.add("Alice"); // 可以修改对象内容
names.remove(0); // 可以修改对象内容
1.3 final成员变量
必须在声明时或构造方法中初始化:
arduino
class MyClass {
final int value1 = 10; // 声明时初始化
final int value2;
MyClass(int v) {
value2 = v; // 构造方法中初始化
}
}
1.4 final静态变量(常量)
通常与static
一起使用定义常量:
arduino
class Constants {
public static final double PI = 3.14159;
public static final String APP_NAME = "MyApp";
}
2. final方法
final
方法不能被子类重写
scala
class Parent {
public final void show() {
System.out.println("这是final方法");
}
}
class Child extends Parent {
// @Override
// public void show() {} // 编译错误,不能重写final方法
}
3. final类
final
类不能被继承:
kotlin
final class FinalClass {
// 类内容
}
// class SubClass extends FinalClass {} // 编译错误,不能继承final类
4. final参数
方法参数被声明为final
,表示在方法内不能修改参数的值:
arduino
public void process(final int input) {
// input = 10; // 编译错误,不能修改final参数
System.out.println(input);
}
5. fianl与性能
使用final
可能会带来一些性能优化:
final
变量可以被JVM优化final
方法在早期Java版本中可以进行内联优化final
类的方法调用都是非虚方法,可以提高执行效率
6. final的最佳实践
- 常量定义:使用
public static final
组合定义为全局常量 - 不可变类:将类申明为
final
,所有字段声明为final
- 线程安全:
final
变量是线程安全的,不需要额外的同步 - 明确设计意图:表明某些内容不应该被修改
7. 注意事项
final
变量必须且只能被赋值一次final
引用变量不能指向其他对象,但是对象内容可以改变- 要创建真正的不可变对象,需要:
- 类申明为
final
- 将所有字段申明为
final
- 不能提供修改字段的方法
- 如果字段是引用类型,返回防御性拷贝
- 类申明为
8. final示例:不可变类
arduino
public final class ImmutablePerson {
private final String name;
private final int age;
private final List<String> hobbies;
public ImmutablePerson(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
this.hobbies = new ArrayList<>(hobbies); // 防御性拷贝
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public List<String> getHobbies() {
return new ArrayList<>(hobbies); // 返回拷贝,保护内部状态
}
}
final关键字是Java中实现不可变性和安全性的主要工具,合理使用final可以提高代码的安全性和可维护性
权限修饰符
权限对比表:
修饰符 | 类内部 | 同包 | 不同包的子类 | 不同包的非子类 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
1. public(公开的)
- 作用范围:所有类均可访问
- 可修饰对象:类、方法、变量、构造器
- 特点:
- 被修饰的成员在任何地方都能访问(包括不同的包)
- 类的public成员可以被其他包的类通过导入后直接使用
2. protected(受保护的)
- 作用范围:同一包内的所有类以及不同包的子类(通过继承访问)
- 可修饰对象:方法、变量、构造器(不能修饰外部类)
- 特点:
- 主要用于支持继承,允许子类访问父类的
protected
成员
- 主要用于支持继承,允许子类访问父类的
3. default(默认,即不写修饰符)
- 作用范围:同一包内的所有类
- 可修饰对象:类、方法、变量、构造器
- 特点:
- 如果未显式指定修饰符,则默认为包级私有
- 不同包的类(即使有继承关系)无法访问
4. private(私有的)
- 作用范围:仅当前类内部
- 可修饰对象:方法、变量、构造器(不能修饰外部类)
- 特点:
- 提供最高级别的封装,外部类(包括子类)无法直接访问
- 通常通过公共的
getter/setter
方法间接访问私有变量
注意事项
- 类的修饰符:
- 外部类只能用
public
或default
(不能是protected/private
) - 内部类可以用所有四种修饰符
- 外部类只能用
- 继承中的权限:
- 子类重写父类方法时,权限不能比父类更严格(例如父类方法是
protected
,子类不能改为private
)
- 子类重写父类方法时,权限不能比父类更严格(例如父类方法是
- 封装的原则:
- 优先使用
private
,通过公共的方法暴露必要的功能(遵循"最小权限原则")
- 优先使用
- 构造器权限:
- 如果构造器是
private
,则只能通过静态工厂方法创建对象(单例模式常用)
- 如果构造器是
通过合理使用修饰符,可以设计出高内聚,低耦合的代码结构
代码块
在代码中,代码块(Code Block)是指{}
括起来的一段代码,用于定义作用域或控制执行流程
1. 普通代码块(局部代码块)
- 作用:限定变量的作用范围(局部变量生命周期)
- 示例:
csharp
public void demo() {
int x = 10;
System.out.println(x); // 正常访问
{ // 普通代码块
int y = 20;
System.out.println(y); // 正常访问
}
// System.out.println(y); // 报错!y 超出作用域
}
2. 静态代码块
- 作用:在类加载的时候执行
- 特点:
- 只执行一次
- 多个静态块按定义顺序执行
- 示例:
dart
class MyClass {
static int num;
static { // 静态代码块
num = 10;
System.out.println("Static block executed.");
}
}
3. 实例代码块
- 作用:在每次创建对象时执行,优先于构造器
- 特点:
- 用于提取多个构造器的公共初始化逻辑
- 多个实例块按定义顺序执行
- 示例:
csharp
class MyClass {
int x;
{ // 实例代码块
x = 100;
System.out.println("Instance block executed.");
}
public MyClass() {
System.out.println("Constructor executed.");
}
}
4. 同步代码块
- 作用:在多线程控制对共享资源的访问(线程安全)
- 示例:
csharp
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 同步代码块
count++;
}
}
}
5. 条件/循环代码块
- 作用:配合
if、for、while
等控制语句的使用 - 示例:
csharp
if (condition) { // if 代码块
System.out.println("Condition is true.");
}
for (int i = 0; i < 5; i++) { // for 代码块
System.out.println(i);
}
关键区别总结
代码块类型 | 执行时机 | 典型用途 |
---|---|---|
普通代码块 | 方法调用时 | 限制变量的作用域 |
静态代码块 | 类加载时(仅一次) | 初始化静态资源 |
实例代码块 | 创建对象时(每次) | 提取构造器公共逻辑 |
同步代码块 | 线程进入synchronized时 | 解决多线程竞争 |
条件/循环代码块 | 控制语句触发时 | 流程控制 |
最佳实践
- 减少代码块嵌套:避免深层嵌套(如超过3层),提高可读性。
- 静态代码块替换复杂的静态初始化:
arduino
static Map<String, String> config;
static {
config = new HashMap<>();
config.put("key1", "value1");
}
- 同步代码块粒度要小:仅锁定必要部分,避免性能问题
通过合理使用代码块
抽象类
抽象类(Abstract Class)是Java中的一种特殊类,它不能被实例化,主要用于定义模版
或提供部分实现
,强制子类实现特定的方法
1. 抽象类的特点
- 不能被实例化(不能new)
- 可以包含抽象方法(无方法体)和普通方法
- 子类必须实现所有抽象方法(除非子类也是抽象类)
- 可以包含成员变量、构造方法、静态方法等。
- 可以定义final方法,防止子类修改。
2. 抽象类的语法
(1)定义抽象类
使用abstract
关键字声明:
csharp
public abstract class Animal {
// 普通成员变量
private String name;
// 构造方法(虽然不能直接实例化,但子类可以调用)
public Animal(String name) {
this.name = name;
}
// 普通方法
public void eat() {
System.out.println(name + " is eating.");
}
// 抽象方法(没有方法体)
public abstract void makeSound();
}
(2)子类继承抽象类
子类必须实现所有的抽象方法:
scala
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造方法
}
@Override
public void makeSound() { // 必须实现抽象方法
System.out.println("Woof! Woof!");
}
}
(3)使用抽象类
typescript
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Buddy");
dog.eat(); // 输出: Buddy is eating.
dog.makeSound(); // 输出: Woof! Woof!
}
}
3. 什么时候使用抽象类
- 多个类有共同代码,但部分行为不同(如Animal有eat(),但是makeSound()不同)
- 需要定义非静态/非final的成员变量
- 需要构造方法初始化状态
- 希望强制子类实现某些方法(如模版方法模式)
4. 经典示例:模版方法模式
抽象类定义算法骨架,子类实现具体步骤:
csharp
abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
// 模板方法(final 防止子类修改算法流程)
public final void play() {
initialize();
startPlay();
endPlay();
}
}
class Cricket extends Game {
@Override
void initialize() { System.out.println("Cricket Game Initialized!"); }
@Override
void startPlay() { System.out.println("Cricket Game Started!"); }
@Override
void endPlay() { System.out.println("Cricket Game Finished!"); }
}
public class Main {
public static void main(String[] args) {
Game game = new Cricket();
game.play(); // 执行模板方法
}
}
5. 抽象类 VS 接口(Java8+)
特性 | 抽象类 | 接口 |
---|---|---|
实例化 | 不能直接实例化 | 不能直接实例化(Java8+可以default 方法) |
方法实现 | 可以有抽象方法和普通方法 | Java7只能有抽象方法,Java8+可以有default 方法 |
变量 | 可以有普通变量和常量 | 只能有public static final 常量 |
构造方法 | 可以有构造方法 | 不能有构造方法 |
多继承 | 只能单继承 | 支持实现多个接口 |
设计目的 | 代码复用+部分实现强制 | 定义行为规范(多态) |
6. 总结
- 抽象类用于代码复用+强制子类实现特定方法
- 接口用于定义行为规范(多继承)
- 优先使用接口,除非需要成员变量,构造方法或非静态方法
- 抽象类适合模版方法模式,定义算法骨架,子类填充细节
合理使用抽象类,可以写出更灵活,可维护的代码!
Java接口(Interface)
接口是Java中一种重要的抽象类,它定义了一组方法签名(抽象方法)的契约,但不提供实现。类可以实现接口(implement
),从而承诺提供接口中所有方法的实现
接口的基本特性
- 完全抽象:接口中的方法默认是抽象的(Java8之前)
- 多实现:一个类可以实现多个接口
- 契约作用:定义行为规范,不关心具体实现
- 不能实例化:不能直接创建接口的示例
接口定义语法
csharp
public interface 接口名 {
// 常量声明 (默认 public static final)
type CONSTANT_NAME = value;
// 方法声明 (默认 public abstract)
returnType methodName(parameterList);
// Java 8+ 默认方法
default returnType methodName() {
// 实现
}
// Java 8+ 静态方法
static returnType methodName() {
// 实现
}
// Java 9+ 私有方法
private returnType methodName() {
// 实现
}
}
接口实现
类使用implement
关键字实现接口:
typescript
public class ClassName implements Interface1, Interface2 {
// 必须实现所有接口的抽象方法
@Override
public returnType methodName() {
// 实现
}
}
接口的新特性
Java8新增
- 默认方法(default methods):
- 使用
default
关键字 - 提供默认实现,实现类可以不重写
- 主要用于接口衍化,避免破坏现有实现
- 使用
csharp
interface Vehicle {
default void print() {
System.out.println("我是一辆车!");
}
}
- 静态方法(static methods)
- 属于接口本身,通过接口名调用
- 不能被子接口或实现类继承
csharp
interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
Java9新增
- 私有方法(private methods)
- 只能在接口内部使用
- 分为私有实例方法和私有静态方法
- 用于提取公共代码,减少重复
csharp
interface DBLogging {
private void createLog() {
// 创建日志的通用代码
}
}
接口和抽象类的区别
特性 | 接口 | 抽象类 |
---|---|---|
方法实现 | Java之前不能有实现 | 可以有具体的实现 |
变量 | 只能public static final 常量 | 可以是普通变量 |
构造方法 | 不能有 | 可以有 |
多继承 | 一个类实现多个接口 | 一个类只能继承一个抽象类 |
设计理念 | "是什么"的契约 | "是什么"的部分实现 |
接口的使用场景
- 定义不相关类之间的共同行为
- 实现多重继承的效果
- 定义回调函数(如事件监听器)
- 实现松耦合的系统设计
- 定义API契约
示例代码:
typescript
// 定义接口
interface Animal {
void eat();
void sleep();
default void breathe() {
System.out.println("呼吸空气");
}
static boolean isAnimal(Object obj) {
return obj instanceof Animal;
}
}
// 实现接口
class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void sleep() {
System.out.println("狗睡在狗窝里");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
dog.sleep();
dog.breathe();
System.out.println(Animal.isAnimal(dog)); // true
}
}
内部类
内部类是定义在另一个类内部的类,它是Java中一种强大的特性,允许将逻辑上相关的类组织在一起,并可以访问外部类的成员
成员内部类
- 定义在外部类的成员位置
- 可以访问外部类的所有成员(包括
private
) - 不能有静态成员(除非是
static final
常量)
csharp
class Outer {
private int x = 10;
class Inner {
void display() {
System.out.println("x = " + x); // 可以访问外部类的私有成员
}
}
}
// 使用方式
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
静态内部类
- 使用
static
修饰的内部类 - 不能直接访问外部类的非静态成员
- 可以看做是一个普通的类,只是定义在另一个类内部
csharp
class Outer {
static int x = 10;
static class StaticNested {
void display() {
System.out.println("x = " + x); // 只能访问外部类的静态成员
}
}
}
// 使用方式
Outer.StaticNested nested = new Outer.StaticNested();
nested.display();
局部内部类
- 定义在方法或作用域块内的类
- 只能访问所在方法中
final
或effectively final
的局部变量 - 作用域限于定义它的代码块
csharp
class Outer {
void outerMethod() {
final int localVar = 20;
class LocalInner {
void display() {
System.out.println("localVar = " + localVar);
}
}
LocalInner inner = new LocalInner();
inner.display();
}
}
匿名内部类
- 没有类名的内部类
- 通常用于创建接口或抽象类的即时代码实现
- 只能创建一个实例
csharp
interface Greeting {
void greet();
}
class Outer {
void sayHello() {
Greeting greeting = new Greeting() { // 匿名内部类
@Override
public void greet() {
System.out.println("Hello, world!");
}
};
greeting.greet();
}
}
内部类的特点
1. 访问权限
- 成员内部类可以访问外部类的所有成员
- 静态内部类只能访问外部类的静态成员
2. .this和.new语法
- OuterClass.this引用外部类实例
- outerObject.new InnerClass()创建内部类实例
3. 编译后文件
- 成员内部类:Outer$Inner.class
- 匿名内部类:Outer$1.class(数字递增)
4. 内存泄露风险
- 内部类隐式持有外部类的引用,可能导致内存泄露
内部类的使用场景
- 逻辑分组:将只在一个地方使用的类逻辑上分组
- 增强封装:访问外部类的私有成员
- 回调机制:实现事件监听器等回调功能
- 多重继承:通过多个内部类模拟多重继承
- GUI开发:Swing/AWT中的事件处理器
示例代码:
csharp
// 综合示例
class Outer {
private int outerField = 10;
private static int staticOuterField = 20;
// 成员内部类
class MemberInner {
void accessOuter() {
System.out.println("访问外部类实例字段: " + outerField);
System.out.println("访问外部类静态字段: " + staticOuterField);
}
}
// 静态嵌套类
static class StaticNested {
void accessOuter() {
// System.out.println(outerField); // 错误,不能访问实例成员
System.out.println("访问外部类静态字段: " + staticOuterField);
}
}
void methodWithLocalClass() {
int localVar = 30; // effectively final
// 局部内部类
class LocalInner {
void display() {
System.out.println("局部变量: " + localVar);
System.out.println("外部类字段: " + outerField);
}
}
new LocalInner().display();
}
void methodWithAnonymousClass() {
// 匿名内部类
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类执行");
System.out.println("访问外部类字段: " + outerField);
}
};
new Thread(r).start();
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
// 成员内部类
Outer.MemberInner memberInner = outer.new MemberInner();
memberInner.accessOuter();
// 静态嵌套类
Outer.StaticNested staticNested = new Outer.StaticNested();
staticNested.accessOuter();
// 局部内部类
outer.methodWithLocalClass();
// 匿名内部类
outer.methodWithAnonymousClass();
}
}