java的面向对象思想
文章目录
- 初识面向对象
- 面向过程&面向对象
- 什么是面向对象
- 方法回顾和加深
- 方法的定义
- 方法的调用
- 类与对象的关系
- 对象的创建分析
- 类与对象小结
- 面向对象三大特性
- 封装
- 封装的好处
- 继承
- super
- super注意点
- 方法重写
- 方法重写注意点
- 多态
- 多态注意点
- instanceof
- instanceof注意点
- 抽象类和接口
- 抽象类
- 抽象类的注意点
- 接口
- 接口的注意点
- 四种内部类详解
- 内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
- 匿名内部类关键点总结
- 等效的传统写法
- 等效的传统写法
初识面向对象
面向过程&面向对象
面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么…
- 面向过程适合处理一些较为简单的问题
面相对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题
对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象来分析整个系统,但是,具体到微观操作,仍然需要面向过程的思路去处理
什么是面向对象
面向对象编程(Object-Oriented Programming,OOP)
面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织(封装)数据
三大特性
- 封装
- 继承
- 多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行角度考虑是先有类后有对象。类是对象的模板
方法回顾和加深
方法的定义
修饰符:一个方法可以使用多个修饰符,如public,static,private,void
返回类型:与定义方法的类型修饰符一致
break:跳出switch。结束循环
return:结束方法,返回一个值,不返回,默认返回空
方法名:注意规范,见名知意
参数列表:可以有多个参数类型和参数名,还可以使用…定义可变长参数
异常抛出:使用throws抛出异常,使用try…catch代码块捕获异常后,抛给上级
方法的调用
静态方法( 和类一起加载):在方法前加上static修饰符,在静态方法内不能调用非静态方法
非静态方法(类实例化后才存在):在方法不加static修饰符,
静态方法和非静态方法的创建时机不同
package com.oop.demo01;public class Demo02 {public static void main(String[] args) {// 实例化这个类 使用new关键字// 对象类型 对象名 = 对象值Student student = new Student();student.say();}// 和类一起加载的public static void a() {
// b(); // 调用时,b()方法还不存在,会报错}// 类实例化后才存在public void b() {}
}
形参和实参:形参的类型和实参的类型要一致
值传递和引用传递
package com.oop.demo01;// 值传递
public class Demo03 {public static void main(String[] args) {int a = 1;System.out.println(a); // 1Demo03.change(a);System.out.println(a); // 1}
// 返回值为空public static void change(int a) {a = 10;}
}
package com.oop.demo01;
// 引用传递(对象):本质还是值传递
public class Demo04 {public static void main(String[] args) {Person person = new Person();System.out.println(person.name); // nullDemo04.change(person);System.out.println(person.name); // baoli}public static void change(Person person) {person.name = "baoli";}
}// 定义一个Persion类,有一个属性 name
class Person {String name; // null
}
this关键字:代表当前类或对象
类与对象的关系
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物
- 动物、植物、手机、电脑…
- Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
对象是抽象概念的具体实例
- 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
- 能够体现出特点,展现出功能的是具体的实例,而不是抽象的概念
对象的创建分析
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的,并且构造器有以下两个特点
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
package com.oop.demo02;public class Application {public static void main(String[] args) {
// 类:抽象的
// 类实例化后会返回一个自己的对象!
// student对象就是一个Student类的具体实例!Student kobe = new Student();Student james = new Student();kobe.name = "kobe";kobe.age = 35;System.out.println(kobe.name);System.out.println(kobe.age);james.name = "james";james.age = 39;System.out.println(james.name);System.out.println(james.age);}
}
package com.oop.demo02;// 学生类
public class Student {
// 属性:字段String name; // nullint age; // 0// 方法public void study() {System.out.println(this.name + "在学习");}
}
创建一个类,默认会有一个无参的构造方法
使用idea生成对应的.class文件
重载构造器
package com.oop.demo02;public class Person {
// 一个类即使什么都不写,也会存在一个构造方法
// 显示的定义构造器String name;// 无参构造器(默认构造器)
// 1、使用new关键字,本质是在调用构造器
// 2、构造器用来初始化值public Person() {}// 有参构造器(重载构造器):一旦定义了有参的构造器,那么必须显示的定义无参的构造器public Person(String name) {this.name = name;}
}
Person类生成的.class文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.oop.demo02;public class Person {String name;public Person() {}public Person(String name) {this.name = name;}
}
main方法(程序主入口)
package com.oop.demo02;public class Application {public static void main(String[] args) {
// 使用new关键字,实例化了一个对象Person person = new Person("baoli");System.out.println(person.name); // baoli}
}
构造器注意点:
- 构造器:
- 名称与类名相同
- 没有返回值,不能写void
- 作用:
- new关键字本质在调用类的构造方法
- 用于初始化对象的值
- 注意点:
- 定义有参构造器后,必须显示的定义一个无参的构造器
- 创建构造器的快捷键:
ALT+insert
类与对象小结
1、类与对象类是一个模板:抽象,对象是一个具体的实例
2、方法定义和调用
3、对象的引用基本类型(8)引用类型:对象是通过引用来操作数据的:栈 ---> 堆
4、属性:字段Field -> 成员变量默认初始化:数字:0 0.0cahr:u0000boolean:false引用:null修饰符 属性类型 属性 = 属性值
5、对象的创建和使用(1)必须使用new关键字创建对象,至少有一个构造器 Person baoli = new Person()(2)调用对象的属性:baoli.name(3)调用对象的方法:baoli.sleep()
6、类:静态的属性——>属性动态的行为——>方法
面向对象三大特性
封装
该露的露,该藏的藏
- 程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
属性私有:get/set
package com.oop.demo02;// 类 private:私有
public class Student {
// 属性私有private String name; // 姓名private int id; // 学号private char sex; // 性别private int age; // 年龄// 提供一些可以操作这个属性的方法(get、set)
// get获取这个数据public String getName() {return this.name;}
// get给设个数据设置值public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {if (age > 120 || age < 0) {this.age = 3;} else {this.age = age;}}
}
main程序入口
package com.oop.demo02;public class Application {public static void main(String[] args) {Student student = new Student();student.setName("baoli");System.out.println(student.getName());student.setAge(999); // 不合法的System.out.println(student.getAge()); // 3}
}
封装的好处
- 提高程序的安全性,保护数据
- 隐藏了代码的时间细节,高内聚,低耦合
- 统一接口
- 提高系统可维护性
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
extend的意思是“扩展”,子类是父类的扩展
java中类只有单继承,没有多继承
继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示
子类和父类之间,从意义上讲应该具有“is a”的关系
Object类
super
父类Person
package com.oop.demo02;// 在java中,所有的类,都默认直接或者间接继承Object
// Person 人:父类
public class Person {protected String name = "james";// 如果是private,则子类也无法继承使用public void print() {System.out.println("Person");}public Person() {System.out.println("Person无参构造器执行了");}
}
子类Student
package com.oop.demo02;// 学生 is 人: 派生类,子类
// 子类继承了父类,就会拥有父类的全部方法
public class Student extends Person {private String name = "kobe";public void print() {System.out.println("Student");}public Student() {
// 隐藏代码:默认调用了父类的无参构造器super(); // 调用父类的构造器,必须放在子类构造器的第一行System.out.println("Student无参构造器执行了");}public void test(String name){System.out.println(name); // baoliSystem.out.println(this.name); // kobeSystem.out.println(super.name); // james}public void test(){print(); // Studentthis.print(); // Studentsuper.print(); // Person}
}
main方法程序主入口
package com.oop.demo02;public class Application {public static void main(String[] args) {Student student = new Student();student.test("baoli"); // baoli kobe jamesstudent.test(); // Student Student Person}
}
super注意点
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super()和this()这两个构造方法不能同时调用
super对比this:
- 代表对象不同
- this:本身调用者这个对象
- super:代表父类对象
- 前提:
- this:没继承也可以使用
- supper:只能在继承条件下才可以使用
- 构造方法:
- this():当前类的构造方法
- super ():父类的构造方法
方法重写
A类(派生类,子类)
package com.oop.demo02;// 继承B类
public class A extends B{public static void test() {System.out.println("A=>test()");}
}
B类(基类,父类)
package com.oop.demo02;// 重写都是方法的重写,和属性无关
public class B {public static void test() {System.out.println("B=>test()");}
}
main()方法主入口
package com.oop.demo02;public class Application {public static void main(String[] args) {
// 方法的调用只和左边定义的数据类型有关A a = new A();a.test(); // A
// 父类的引用指向了子类B b = new A();b.test(); // B}
}
方法重写对象方法
A类(派生类,子类)
package com.oop.demo02;// 继承B类
public class A extends B{
// @Override 重写@Override // 注解:有功能的注释public void test() {
// super.test(); 默认生成System.out.println("A=>test()");}
}
B类(基类,父类)
package com.oop.demo02;// 重写都是方法的重写,和属性无关
public class B {public void test() {System.out.println("B=>test()");}
}
main()方法主入口
package com.oop.demo02;public class Application {public static void main(String[] args) {
// 方法的调用只和左边,定义的数据类型有关
// 非静态的方法可以重写A a = new A();a.test(); // A
// 父类的引用指向了子类B b = new A();b.test(); // A}
}
方法重写注意点
-
方法没有static修饰时,b调用的是对象的方法,而b是用A类new的
-
方法有static修饰时,b调用了B类的方法,因为b是用B类定义的
-
重写需要有继承关系,子类重写父类的方法
-
方法名必须相同,参数列表必须相同,方法体不同
-
修饰符:范围可以扩大但不能缩小: public >protected>default>private
-
抛出的异常:范围可以被缩小,但不能扩大:ClassNotFount -> Exception
多态
- 即同一方法可以根据发送对象的不同而采用多种不同的行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型很多
- 多态存在的条件
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态性
- instanceof
- 引用类型转换
Student子类
package com.oop.demo03;public class Student extends Person {@Override // 重写了父类的run方法public void run() {System.out.println("Student run");}public void eat() {System.out.println("Student eat");}
}
Person父类
package com.oop.demo03;public class Person {public void run() {System.out.println("Person run");}
}
main方法主入口
package com.oop;
import com.oop.demo03.Person;
import com.oop.demo03.Student;public class Application {public static void main(String[] args) {
// 一个对象的实际类型是确定的
// new Student();
// new Person();
// 但可以指向的引用类型就不确定了,父类的引用指向子类// Student 能调用的方法都是自己的或者继承父类的方法Student s1 = new Student();
// Person 父类,可以指向子类,但是不能调用子类独有的方法(子类有,父类没有)Person s2 = new Student();Object s3 = new Student();// 对象能执行哪些方法,主要看对象左边的类型s1.run(); // Student run// 子类重写了父亲的方法,执行子类的方法s2.run(); // Student runs1.eat(); // Student eat}
}
多态注意点
- 子类重写了方法的前提下,只要new的是子类实例,无论实例前声明的是超类还是子类的引用,都会调用子类自己重写过的方法
- 多态是方法的多态,属性没有多态
- 父类和子类,类型转换异常!ClassCastException
- 存在多态的必要条件:有继承关系,方法需要重写,父类引用指向子类对象! Father f1 = new Son()
- 不能重写的方法
- static方法,属于类方法,不属于实例方法,不能重写
- final修饰符 修饰的方法
- private修饰符 修饰的方法
instanceof
package com.oop;
import com.oop.demo04.Person;
import com.oop.demo04.Student;public class Application {public static void main(String[] args) {
// Object > String
// Object > Person > Teacher
// Object > Person > Student
// 编译看左边,运行看右边,左边是右边的实例或本身,返回true否则返回falseObject object = new Student();System.out.println(object instanceof Student); // trueSystem.out.println(object instanceof Person); // trueSystem.out.println(object instanceof Object); // trueSystem.out.println(object instanceof Tearcher); // falseSystem.out.println(object instanceof String); // falseSystem.out.println("================================");Person person = new Student();System.out.println(person instanceof Student); // trueSystem.out.println(person instanceof Person); // trueSystem.out.println(person instanceof Object); // trueSystem.out.println(person instanceof Tearcher); // false
// System.out.println(person instanceof String); // 编译报错System.out.println("================================");Student student = new Student();System.out.println(student instanceof Student); // trueSystem.out.println(student instanceof Person); // trueSystem.out.println(student instanceof Object); // true
// System.out.println(student instanceof Tearcher); // 编译报错
// System.out.println(student instanceof String); // 编译报错}
}
Student子类
package com.oop.demo04;public class Student extends Person {public void go() {System.out.println("Student go");}
}
Person父类
package com.oop.demo04;public class Person {public void run() {System.out.println("Person run");}
}
main()主入口
package com.oop;
import com.oop.demo04.Person;
import com.oop.demo04.Student;public class Application {public static void main(String[] args) {
// 类型之间的转化:父 ---> 子
// 高 低Person person = new Student();
// 将这个对象转换为Student类型,我们就可以使用Student类型的方法了Student student = (Student) person; // 父类转子类,高转低->强制转换// 调用子类的go方法student.go(); // Student go// 调用父类的run方法student.run(); // Person runSystem.out.println("===========================");
// 子类转父类,可能丢失自己本来独有的一些方法!Student student1 = new Student();student1.go(); // 转换前可以调用子类独有的go方法Person person1 = student1; // 子类转父类,低转高->自动转换// 转换后不能调用转换前的子类独有的go方法,只能调用父类独有的run方法person1.run();}
}
如果子类中重写了父类的方法
Student子类重写父类的run方法
package com.oop.demo04;public class Student extends Person {public void go() {System.out.println("Student go");}@Overridepublic void run() {System.out.println("Student run");}
}
main()主入口
package com.oop;
import com.oop.demo04.Person;
import com.oop.demo04.Student;public class Application {public static void main(String[] args) {
// 类型之间的转化:父 ---> 子
// 高 低Person person = new Student();
// 将这个对象转换为Student类型,我们就可以使用Student类型的方法了Student student = (Student) person; // 父类转子类,高转低->强制转换// 调用子类的go方法student.go(); // Student go// 调用被子类重写的run方法student.run(); // Student runSystem.out.println("===========================");
// 子类转父类,可能丢失自己本来的一些特有方法!Student student1 = new Student();student1.go(); // 转换前可以调用go方法Person person1 = student1; // 子类转父类,低转高->自动转换// 转换后,不能调用转换之前子类独有的go方法person1.run(); // 可以调用被子类重写的run方法}
}
instanceof注意点
- 子转父: 向上转型, 直接转, 丢失子类中原本可直接调用的特有方法; 父转子: 向下转型, 强制转, 丢失父类被子类所重写掉的方法。
抽象类和接口
抽象类
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
抽象类,不能使用new关键字来创建对象,他是用来让子类继承的
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
子类继承抽象类,那么就必须要实现抽象类,没有实现的抽象方法,否则该子类也要声明为抽象类
子类A
package com.oop.demo05;// 继承了抽象类,就必须实现抽象类中的所有抽象方法,除非自己也是抽象类
public class A extends Action{@Overridepublic void doSomething() {}
}
抽象类:父类Action
package com.oop.demo05;// abstract修饰类,代表这个类是抽象类
public abstract class Action {
// abstract修饰方法,代表这个方法是抽象方法
// 抽象方法:只有方法名字,没有方法实现
// abstract可以作为一个约束,自己不用实现,有自己的后代帮我们实现public abstract void doSomething();public void hello() {System.out.println("hello abstract");}
}
抽象类的注意点
- 有抽象方法的类一定是抽象类,抽象类里不一定有抽象方法
- 不能new这个抽象类,只能靠子类去实现它
- 抽象类中可以写普通方法
- 抽象方法必须在抽象类中
- 抽象类不能被实例化,抽象类中没有构造方法
接口
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范!自己无法写方法,约束和实现分离,面向接口编程
- 接口就是规范,定义的是一组规则
- 接口本质是契约,就想我们人间的法律一样,制定好后大家都遵守
- 面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口
UserService接口
package com.oop.demo06;// interface 定义的关键字, 接口都需要有实现类
public interface UserService {
// 接口中定义的属性,默认都是静态常量,默认加了 public static finalint AGE = 99;// 接口中的所有定义的方法,其实都是抽象的,定义的方法默认加了 public abstract修饰符void add(String name);void delete(String name);void update(String name);void query(String name);
}
TimeService接口
package com.oop.demo06;public interface TimeService {void timer();
}
UserServiceImpl.java
package com.oop.demo06;// 抽象类只能使用extends实现方法,所以是单继承
// 类 可以实现接口 使用 implements关键字实现接口
// 实现了接口的类,就需要实现接口中的方法
// 利用接口实现多继承
public class UserServiceImpl implements UserService,TimeService{@Overridepublic void add(String name) {}@Overridepublic void delete(String name) {}@Overridepublic void update(String name) {}@Overridepublic void query(String name) {}@Overridepublic void timer() {}
}
接口的注意点
- 接口都是约束
- 定义的方法默认都是抽象方法,让不同的人实现 -> public abstract
- 定义的属性默认都是静态常量 -> public static final
- 接口不能被实例化,接口中没有构造方法
- implements可以实现多个接口,必须要重新多个接口中的所有方法
四种内部类详解
内部类
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
成员内部类
package com.oop.demo07;// 外部类
public class Outer {private int id = 10;public void out() {System.out.println("这是外部类的out方法");}// 成员内部类public class Inner {public void in() {System.out.println("这是成员内部类的in方法");}// 获得外部类的私有属性或私有方法public void getId() {System.out.println(id);}}
}
main()程序主入口
package com.oop;
import com.oop.demo07.Outer;public class Application {public static void main(String[] args) {// 成员内部类是外部类的实例,因此需要先创建外部类的对象即可实例化
// 通过这个外部类来实例化内部类Outer outer = new Outer();Outer.Inner inner = outer.new Inner();inner.in(); // 这是成员内部类的in方法inner.getId(); // 10}
}
成员内部类注意点
- 内部类可以直接访问外部类方法和属性,不需创建外部类的对象
- **成员内部类是外部类的实例,因此需要先创建外部类的对象即可实例化 **
静态内部类
如果内部类是用static修饰的,要访问外部类的属性,那么外部类的属性也需要加上static修饰
外部类Outer
package com.oop.demo07;public class Outer {// 加上static,静态内部类就可以访问这个属性private static int id = 10;public void out() {System.out.println("这是外部类的out方法");}// 静态内部类(嵌套类)public static class Inner {public void in() {System.out.println("这是静态内部类的in方法");}// 获得外部类的私有属性或私有方法public void getId() {System.out.println(id);}}
}
main()程序主入口
package com.oop;
import com.oop.demo07.Outer;public class Application {public static void main(String[] args) {Outer outer = new Outer();// 静态内部类属于外部类本身,而不是外部类的实例,因此不需要先创建外部类的对象即可实例化。// 直接实例化静态内部类,无需创建 Outer 的实例Outer.Inner inner = new Outer.Inner();inner.in(); // 这是静态内部类的in方法new Outer().out(); // 这是外部类的out方法}
}
静态内部类的注意点
- **静态内部类(也称为嵌套类)可以被实例化,但实例化方式与非静态内部类(成员内部类)不同。 **
- **静态内部类属于外部类本身,而不是外部类的实例,因此不需要先创建外部类的对象即可实例化。 **
- **直接实例化静态内部类,无需创建 Outer 的实例 **
局部内部类
Outer.java
package com.oop.demo07;public class Outer {
// 局部内部类public void method() {class Inner {public void in() {System.out.println("这是局部内部类的in方法");}}// 只能在 method() 方法内部实例化 Inner 类Inner inner = new Inner();inner.in(); // 调用内部类的方法}
}
main()程序主入口
package com.oop;
import com.oop.demo07.Outer;public class Application {public static void main(String[] args) {Outer outer = new Outer();// 调用 method() 时才会执行 Inner 类的实例化和方法调用outer.method(); // 这是局部内部类的in方法}
}
局部内部类的注意点
- 局部内部类的作用域Inner类只能在 method()方法内部被访问,外部无法直接引用。
- **必须在 method()方法的内部创建 Inner 的实例,因为该类的定义只在方法的作用域内有效。 **
匿名内部类
匿名内部类的第一种形式(new 类名() {})
package com.oop.demo07;public class Test {public static void main(String[] args) {// 使用了匿名内部类的语法来创建 Apple 类的实例new Apple().eat(); // 普通的顶级类的eat方法Apple apple = new Apple() {@Overridepublic void eat() {
// super.eat(); // 重写eat方法默认生成System.out.println("这才是匿名内部类中重写的eat方法");}};apple.eat(); // 这才是匿名内部类中重写的eat方法new Apple() {@Overridepublic void eat() {
// super.eat(); // 重写eat方法默认生成System.out.println("这才是匿名内部类中重写的eat方法");}}.eat(); // 这才是匿名内部类中重写的eat方法}
}// Apple 是一个普通的顶级类(非内部类)
class Apple {public void eat() {System.out.println("普通的顶级类的eat方法");}
}
-
匿名内部类的本质
这行代码创建了一个继承自Apple
的匿名子类,并立即实例化该子类的对象。这个子类重写了eat()
方法,因此调用eat()
时会执行重写后的逻辑 -
对象的类型
虽然对象的引用类型是Apple
,但实际类型是匿名子类。例如:
Apple apple = new Apple() {@Overridepublic void eat() {System.out.println("匿名内部类");}
};System.out.println(apple.getClass()); // 输出: class com.oop.demo07.Test$1
Test$1
表示这是 Test
类的第一个匿名内部类。
- 多态的体现
通过父类Apple
的引用调用eat()
方法时,实际执行的是匿名子类中重写的方法(动态绑定)。
匿名内部类关键点总结
new Apple(){...}
返回对象:它是匿名子类的实例,也是Apple
类的子类对象。new Apple(){...}
返回的是一个对象实例,属于匿名内部类类型。这个匿名内部类继承自Apple
类,并在创建时重写了eat()
方法。- 匿名内部类的特点
- 没有显式的类名,直接在
new
语句中定义。 - 必须继承某个类或实现某个接口。
- 适合创建一次性使用、需要自定义行为的子类。
- 没有显式的类名,直接在
- 与普通子类的区别:
匿名内部类无需单独定义类文件,语法更简洁,但只能使用一次。
等效的传统写法
这段代码等效于先定义一个具名子类,再实例化它:
// 定义具名子类
class SpecialApple extends Apple {@Overridepublic void eat() {System.out.println("具名子类的eat方法");}
}// 实例化具名子类
new SpecialApple().eat();
匿名内部类的第二种形式(new 接口名(){})
package com.oop.demo07;public class Test {public static void main(String[] args) {UserService userService = new UserService() {@Overridepublic void hello() {System.out.println("我是接口实现的匿名内部类");}};userService.hello(); // 我是接口实现的匿名内部类new UserService() {@Overridepublic void hello() {System.out.println("我是接口实现的匿名内部类");}}.hello(); // 我是接口实现的匿名内部类}
}interface UserService {void hello();
}
userService
是一个对象实例,属于匿名内部类类型,在创建对象的同时实现了 UserService
接口
UserService userService = new UserService() {@Overridepublic void hello() {System.out.println("我是接口实现的匿名内部类");}
};
userService.hello(); // 我是接口实现的匿名内部类
匿名内部类的本质
-
这行代码实际上创建了一个没有显式类名的类,该类实现了
UserService
接口 -
对象的类型
userService
是UserService
接口的实例,但具体类型是匿名内部类,匿名内部类会隐式实现该接口。 -
对象的行为
userService
对象的hello()
方法会执行匿名内部类中重写的逻辑:userService.hello(); // 输出:我是接口实现的匿名内部类
等效的传统写法
这段代码等效于先定义一个具名类,再实例化它:
// 定义一个实现 UserService 接口的具体类
class MyUserService implements UserService {@Overridepublic void hello() {System.out.println("我是接口实现的匿名内部类");}
}// 实例化具名类
UserService userService = new MyUserService();
匿名内部类注意点
- 匿名内部类:需要在 new 关键字后紧跟类名(或接口名),并通过 {…}定义类的内容(如方法重写)
- 匿名内部类的核心特点是没有显式的类名,并且在创建时直接实现或继承某个类 / 接口
- 匿名内部类的优势在于无需显式定义类名,适合简化一次性使用的子类实现,也适合一次性使用的场景(如事件监听器、回调函数)