06-面向对象高级02

张开发
2026/5/12 20:38:02 15 分钟阅读

分享文章

06-面向对象高级02
1. 接口1.1. 接口介绍接口体现的是对规则的声明它定义了某个类型应该具备哪些能力但通常不直接规定这些能力的具体实现方式。Java 中的接口更多体现的是对行为的抽象。接口用关键字interface来定义public interface 接口名 {}接口不能实例化接口和类之间是实现关系通过implements关键字表示public class 类名 implements 接口名 {}接口的子类实现类要么重写接口中的所有抽象方法要么是抽象类接口的最大价值在于 解耦即 调用方不需要关心具体是哪一个实现类只关心“你是否符合这个规范”。interface Usb { void connect(); } // 实现类 class Mouse implements Usb { Override public void connect() { System.out.println(鼠标已连接); } } class Keyboard implements Usb { Override public void connect() { System.out.println(键盘已连接); } } // 使用者 class Computer { public void useDevice(Usb device) { device.connect(); } } // 测试 public class Test { public static void main(String[] args) { Computer c new Computer(); c.useDevice(new Mouse()); c.useDevice(new Keyboard()); } }这里Computer不需要知道是鼠标还是键盘只要设备实现了Usb接口即可。public abstract class Demo01 { public abstract void method1(); public abstract void method2(); } public interface Inter { public abstract void method3(); // void method3(); 一个意思因为默认的就是 public abstract public abstract void method4(); }1.2. 接口中的成员特点在 JDK8 之前接口里主要包含两类成员常量抽象方法interface Flyable { int MAX_SPEED 7900; void fly(); }实际上这里的成员默认带有固定修饰符接口中的属性默认是public static final接口中的方法默认是public abstract所以上面代码等价于interface Flyable { public static final int MAX_SPEED 7900; public abstract void fly(); }1.3. 类实现接口类通过implements来实现接口。interface Runner { void run(); } class Person implements Runner { Override public void run() { System.out.println(人在跑步); } }一个普通类实现接口后必须实现接口中的所有抽象方法否则该类必须声明为抽象类。一个类可以实现多个接口Java 类是单继承的一个类只能继承一个父类但一个类可以实现多个接口。interface A { void methodA(); } interface B { void methodB(); } class C implements A, B { Override public void methodA() { System.out.println(实现A); } Override public void methodB() { System.out.println(实现B); } }1.4. 类和接口之间的关系类和类的关系继承关系只能单继承但是可以多层继承类和接口的关系实现关系可以单实现也可以多实现还可以在继承一个类的同时实现多个接口接口和接口的关系继承关系可以单继承也可以多继承1.5. 抽象类和接口的对比成员变量抽象类可以定义变量也可以常量接口只能定义常量成员方法抽象类可以的是定义具体方法也可以定义抽象方法接口只能定义抽象方法构造方法抽象类 有接口 无1.6. 应用场景接口可以为程序制定规则代码更加规范费~下面是例子package jiekou; /** * 订单业务接口 */ public interface OrderService { /** * 创建订单 */ void create(); /** * 查询单个订单 */ void findOne(); /** * 查询订单列表 */ void findList(); /** * 取消订单 */ void cancel(); /** * 完结订单 */ void finish(); /** * 支付订单 */ void paid(); }package jiekou; public class OrderServiceImpl implements OrderService { Override public void create() {} Override public void findOne() {} Override public void findList() {} Override public void cancel() {} Override public void finish() {} Override public void paid() {} }总结抽象类对事物做抽象(描述事物)接口对行为抽象(制定规则)1.7. 接口新特性JDK8接口中可以定义有方法体的方法。默认、静态接口中允许定义static静态方法注意1、静态方法只能通过接口名调用不能通过实现类名或者对象名调用2、public可以省略static不能省略JDK9接口中可以定义私有方法。2. 多态同一个行为具有多个不同表现形态或形态的能力2.1. 多态的前提1、有继承 / 实现关系2、有方法重写3、有父类引用指向字类对象2.2. 多态的成员的访问特点成员变量编译看左边父类执行看左边父类成员方法编译看左边父类执行看右边子类静态成员编译看父类运行看父类2.3. 好处和弊端好处提高了程序的扩展性对象多态将方法的形参定义为父类类型,这个方法可以接收该父类的任意子类对象行为多态同一个行为,具有多个不同表现形式或形态的能力弊端不能使用子类的特有成员2.4. 多态中的转型如果被转的引用类型变量对应的实际类型和目标类型不是同一种类型那么在转换的时候就会出现ClassCastException关键字instanceof使用格式对象名 instanceof 类型判断一个对象是否是一个类的实例通俗的理解判断关键字左边的对象是否是右边的类型返回boolean类型结果例子假设我们有一个父类 Shape和两个子类 Circle圆形和 Rectangle矩形。我们需要写一个方法根据形状的不同计算面积或打印特定信息。class Shape { String name; Shape(String name) {this.name name;} } class Circle extends Shape { double radius; public Circle(double radius) { super(Circle); this.radius radius; } } class Rectangle extends Shape { double width, height; public Rectangle(double width, double height) { super(Rectangle); this.width width; this.height height; } } public class InstanceofDemo { public static void main(String[] args) { Shape s1 new Circle(5.0); Shape s2 new Rectangle(4.0, 6.0); Shape s3 new Shape(UnKnown); describeShape(s1); describeShape(s2); describeShape(s3); } // 使用新模式匹配 public static void describeShape(Shape shape) { if (shape instanceof Circle c) { System.out.println(圆形半径为: c.radius); } else if (shape instanceof Rectangle r) { System.out.println(矩形宽度为: r.width , 高度为: r.height); } else { System.out.println(未知形状: shape.name); } } }2.4.1. Object 中的 equals 方法比较两个对象是否相同存在的意义父类 equals 方法存在的意义就是为了被子类重写以便子类自己来定制比较规则如果你在自己的类中没有重写overrideequals() 方法那么你的类将直接使用 Object 类中的默认实现。public boolean equals(Object obj) { return (this obj); }对于对象而言 比较的是内存地址即引用是否指向堆内存中的同一个对象。在默认情况下只有当两个引用变量指向完全同一个对象实例时equals() 才返回 true。即使两个对象的内容字段值完全一样只要它们是不同的实例默认的 equals() 也会返回 false。class Person { String name; Person(String name) { this.name name;} } public class EqualsDemo { public static void main(String[] args) { Person p1 new Person(Alice); Person p2 new Person(Alice); System.out.println(p1 p2); // false 不同地址 System.out.println(p1.equals(p2)); // false 默认实现比较地址 } }但是对于大多数业务类如 String, Integer, 或自定义的 User, Product 等我们通常关心的是对象内部的数据内容是否相同而不是它们是否是同一个实例。一般会重写Java 的 String 类重写了 equals() 方法。String s1 new String(hello); String s2 new String(hello); System.out.println(s1 s2); // false System.out.println(s1.equals(s2)); // trueQ 号和 equals 的区别A可以比较基本数据类型也可以比较引用数据类型【基本类型比较数据值引用类型比较地址值】如果不希望比较地址值可重写该方法自定义比较规则3. 代码块4. package 包包本质来说就是文件夹用来管理类文件的建包的语法格式package 公司域名倒写.技术名称。包名建议全部英文小写且具备意义相同包下的类可以直接访问不同包下的类必须导包,才可以使用导包格式import包名.类名;如果导入的类在java.lang包(核心包), 就不需要编写import导包代码假如一个类中需要用到不同类而这个两个类的名称是一样的那么默认只能导入一个类另一个类要带包名访问5. 内部类内部类就是定义在一个类里面的类【封装性更好但是用起来麻烦建议少用~】class Outer { // 内部类 class Inner {} } // 创建对象的格式 Outer.Inner in new Outer().Inner();特点内部类中访问外部类成员直接访问包括私有外部类中访问内部类成员需要创建对象访问5.1. 分类成员内部类、静态内部类、局部内部类、匿名内部类静态内部类class Outer { static class Inner {} } // 创建对象格式 Outer.Inner in new Outer.Inner();局部内部类【鸡肋】局部内部类放在方法、代码块、构造器等执行体中匿名内部类【这个用的多一点】匿名内部类本质上是一个特殊的局部内部类定义在方法内部前提需要存在一个接口或类匿名内部类可以使代码更加简洁定义一个类的同时对其进行实例化例子启动线程最经典的方式就是使用匿名内部类来实现 Runnable 接口。public class AnonymousInnerClassDemo { public static void main(String[] args) { // 语法: new 接口 / 父类名() {重写方法...} // 看起来像是在实例化接口接口不能实例化 // 但实际上这里是在定义一个实现了 Runnable 的匿名子类并立即创建它的实例。 Runnable task new Runnable() { // 必须实现接口中的抽象方法 Override public void run() { System.out.println(匿名内部线程启动了); System.out.println(当前线程名: Thread.currentThread().getName()); } }; // 注意最后有一个分号因为整个 new ... { ... } 表达式最终返回的是一个对象引用。 new Thread(task).start(); } }6. Lambda 表达式作用简化匿名内部类的代码写法。它的核心思想是只关心“做什么”不关心“类名”和“方法名”。什么是函数式接口首先必须是接口、其次接口中有且仅有一个抽象方法的形式通常我们会在接口上加上一个FunctionalInterface注解标记该接口必须是满足函数式接口。FunctionalInterface interface ShowHandler { void show(); }省略写法参数类型可以省略不写如果只有一个参数参数类型可以省略同时()也可以省略。如果Lambda表达式的方法体代码只有一行代码可以省略大括号不写,同时要省略分号此时如果这行代码是return语句必须省略return不写同时也必须省略;不写例子 1public class LambdaDemo { public static void main(String[] args) { useShowHandler(new ShowHandler() { Override public void show() { System.out.println(匿名内部类重写后的show方法.....); } }); // useShowHandler(() - { // System.out.println(Lambda表达式重写后的show方法.....); // }); // 省略 useShowHandler(() - System.out.println(Lambda表达式重写后的show方法.....)); } // 因为 main 方法是 static 的 // 而静态方法只能直接调用其他静态方法或访问静态变量不能直接调用非静态方法实例方法。 public static void useShowHandler(ShowHandler showHandler) { showHandler.show(); } } FunctionalInterface interface ShowHandler { void show(); }例子 2public class LambdaDemo1 { public static void main(String[] args) { userStingHandler(new StringHandler() { Override public void printMessage(String msg) { System.out.println(匿名内部类重写后的printMessage方法.....); } }); // userStingHandler((String msg) - { // System.out.println(Lambda表达式重写后的printMessage方法.....); // }); // 简写 userStingHandler(msg- System.out.println(Lambda表达式重写后的printMessage方法.....)); } public static void userStingHandler(StringHandler stringHandler) { stringHandler.printMessage(hello world); } } FunctionalInterface interface StringHandler { void printMessage(String msg); }例子 3import java.util.Random; public class LambdaDemo2 { public static void main(String[] args) { useRandomNumber(new RandomNumberHandler() { Override public int getNumber() { return new Random().nextInt(100) 1; } }); // useRandomNumber(() - { // return new Random().nextInt(100) 1; // }); useRandomNumber(() - new Random().nextInt(100) 1); } public static void useRandomNumber(RandomNumberHandler randomNumberHandler) { int result randomNumberHandler.getNumber(); System.out.println(result); } } FunctionalInterface interface RandomNumberHandler { int getNumber(); }例子 4public class LambdaDemo3 { public static void main(String[] args) { useCalculator(new Calculator() { Override public int calc(int a, int b) { return a b; } }); // useCalculator((int a, int b) - { // return a b; // }); useCalculator((a, b) - a b); } public static void useCalculator(Calculator calculator) { int result calculator.calc(10, 20); System.out.println(result); } } FunctionalInterface interface Calculator { int calc(int a, int b); }6.1. 与匿名内部类的区别使用限制不同匿名内部类:可以操作类、接口Lambda表达式:只能操作函数式接口实现原理不同匿名内部类编译之后产生一个单独的.class字节码文件Lambda表达式编译之后没有一个单独的.class字节码文件

更多文章