Java基础:面向对象编程4

server/2024/10/15 16:56:46/

1 Java 访问修饰符

1.1 概述

Java 提供了四种访问权限控制:

  • 默认访问权限(包访问权限)
  • public
  • private
  • protected

类只能使用默认访问权限和 public 修饰,而变量和方法则可以使用所有四种修饰符。

1.2 修饰类

  • 默认访问权限(包访问权限):修饰类时,表示该类只对同一个包中的其他类可见。
  • public:修饰类时,表示该类对所有其他类都可见。

1.3 修饰方法和变量

  • 默认访问权限(包访问权限):如果一个类的方法或变量被包访问权限修饰,只能在同一个包中的其他类中显式调用该类的方法或变量,不同包中的类不能显式调用。
  • private:如果一个类的方法或变量被 private 修饰,只能在类本身中访问,类外以及其他类中都不能显式访问。
  • protected:如果一个类的方法或变量被 protected 修饰,对于同一个包的类,该类的方法或变量可以被访问。对于不同包的类,只有继承于该类的类才可以访问该类的方法或变量。
  • public:被 public 修饰的方法或变量,在任何地方都是可见的。

1.4 关于 Java 包和类文件的补充知识

  • 包的作用:主要是为了防止类文件命名冲突以及方便代码组织和管理。
  • public 类:对于一个 Java 源代码文件,如果存在 public 类,只能有一个 public 类,且源代码文件的名称必须与 public 类的名称完全相同。
  • 非 public 类:如果源代码文件没有 public 类,则源代码文件的名称可以随意命名。

1.5 示例代码

1.5.1 默认访问权限(包访问权限)

java">// 文件名: DefaultAccessExample.java
package com.example.package1;class DefaultAccessExample {int value = 10; // 默认访问权限void printValue() {System.out.println(value);}
}// 文件名: AnotherClass.java
package com.example.package1;public class AnotherClass {public static void main(String[] args) {DefaultAccessExample obj = new DefaultAccessExample();obj.printValue(); // 可以访问,因为它们在同一个包中}
}

1.5.2 public 类

java">// 文件名: PublicClassExample.java
package com.example.package2;public class PublicClassExample {public int value = 20; // public 变量public void printValue() {System.out.println(value);}
}// 文件名: MainClass.java
package com.example.package3;public class MainClass {public static void main(String[] args) {PublicClassExample obj = new PublicClassExample();obj.printValue(); // 可以访问,因为 PublicClassExample 是 public 类}
}

1.5.3 private 修饰符

java">// 文件名: PrivateAccessExample.java
package com.example.package4;public class PrivateAccessExample {private int value = 30; // private 变量private void printValue() {System.out.println(value);}public void accessPrivateMethod() {printValue(); // 在类内部可以访问 private 方法}
}// 文件名: MainClass.java
package com.example.package4;public class MainClass {public static void main(String[] args) {PrivateAccessExample obj = new PrivateAccessExample();// obj.printValue(); // 编译错误,无法访问 private 方法obj.accessPrivateMethod(); // 通过 public 方法间接访问 private 方法}
}

1.5.4 protected 修饰符

java">// 文件名: ProtectedAccessExample.java
package com.example.package5;public class ProtectedAccessExample {protected int value = 40; // protected 变量protected void printValue() {System.out.println(value);}
}// 文件名: SubClass.java
package com.example.package6;import com.example.package5.ProtectedAccessExample;public class SubClass extends ProtectedAccessExample {public void accessProtectedMethod() {printValue(); // 可以访问,因为 SubClass 继承了 ProtectedAccessExample}
}// 文件名: MainClass.java
package com.example.package6;public class MainClass {public static void main(String[] args) {SubClass obj = new SubClass();obj.accessProtectedMethod(); // 通过继承访问 protected 方法}
}

2 Java 代码初始化块

2.1 概念

代码初始化块(也称为实例初始化块)用于初始化类的成员变量。对象在创建时会执行代码初始化块。代码初始化块主要用于区分静态初始化块。

  • 实例初始化块:在对象创建时执行,用于初始化实例变量。
  • 静态初始化块:在类加载时执行,用于初始化静态变量。

通过代码初始化块,可以执行更多的初始化操作,例如打印出成员变量初始化后的值。

2.2 代码初始化规则

  • 类实例化时执行:代码初始化块在对象创建时执行。
  • 放在构造方法中执行:实际上,代码初始化块是放在构造方法中执行的,但位于其他代码之前。
  • 执行顺序:代码初始化块的执行顺序是从前到后的。
  • 父类构造方法优先:在默认情况下,子类的构造方法在执行时会先调用父类的构造方法,然后再执行代码初始化块。

2.3 示例代码

以下示例演示了实例初始化块和静态初始化块的用法。

java">public class InitializationBlockExample {// 静态变量static int staticVar;// 实例变量int instanceVar;// 静态初始化块static {staticVar = 10;System.out.println("静态初始化块执行: staticVar = " + staticVar);}// 实例初始化块{instanceVar = 20;System.out.println("实例初始化块执行: instanceVar = " + instanceVar);}// 构造方法public InitializationBlockExample() {System.out.println("构造方法执行");}public static void main(String[] args) {System.out.println("创建第一个对象:");InitializationBlockExample obj1 = new InitializationBlockExample();System.out.println("创建第二个对象:");InitializationBlockExample obj2 = new InitializationBlockExample();}
}

2.3.1 输出结果

静态初始化块执行: staticVar = 10
创建第一个对象:
实例初始化块执行: instanceVar = 20
构造方法执行
创建第二个对象:
实例初始化块执行: instanceVar = 20
构造方法执行

2.3.2 解释

  • 静态初始化块:在类加载时执行,只会执行一次,并且优先于实例初始化块和构造方法的执行。
  • 实例初始化块:在每次创建对象时执行,在构造方法之前执行。

2.4 小结

  • 代码初始化块(实例初始化块)用于初始化实例变量,在对象创建时执行,位于构造方法之前。
  • 静态初始化块用于初始化静态变量,在类加载时执行,只会执行一次,优先于实例初始化块和构造方法。

3 Java 抽象类

3.1 定义抽象类

定义抽象类时需要使用 abstract 关键字,放在 class 关键字前。例如:

java">abstract class AbstractPlayer { 
}

命名规范:抽象类的命名通常以 AbstractBase 开头,以明确其抽象性质。

3.2 抽象类的特征

  • 不能实例化:抽象类不能通过 new 关键字实例化。尝试实例化会报错。
  • 可以有子类:抽象类可以有子类,子类通过 extends 关键字继承抽象类。例如:
    java">public class BasketballPlayer extends AbstractPlayer { 
    }
    
  • 必须包含抽象方法:如果一个类定义了一个或多个抽象方法,那么这个类必须是抽象类。
  • 抽象方法:抽象类中可以定义抽象方法和普通方法。抽象方法没有方法体,只有方法签名。
  • 子类实现抽象方法:抽象类的子类必须实现父类中定义的抽象方法。例如:
    java">abstract class AbstractPlayer {abstract void play();
    }public class BasketballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("BasketballPlayer is playing basketball.");}
    }
    

3.3 抽象类的应用场景

  • 复用通用功能:当希望多个子类复用某些通用功能时,可以使用抽象类。例如,抽象类 AbstractPlayer 中有一个普通方法 sleep(),所有运动员都需要休息,这个方法可以被子类复用。
  • 定义 API 并扩展实现:当需要在抽象类中定义好 API,然后在子类中扩展实现时,可以使用抽象类。例如,抽象类 AbstractPlayer 中定义了一个抽象方法 play(),所有运动员都可以从事某项运动,但需要对应子类去扩展实现。

3.4 示例代码

3.4.1 示例代码1

3.4.1.1 抽象类定义
java">abstract class AbstractPlayer {// 普通方法public void sleep() {System.out.println("Player is sleeping.");}// 抽象方法abstract void play();
}
3.4.1.2 子类实现
java">public class BasketballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("BasketballPlayer is playing basketball.");}
}public class FootballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("FootballPlayer is playing football.");}
}
3.4.1.3 使用抽象类
java">public class Main {public static void main(String[] args) {BasketballPlayer basketballPlayer = new BasketballPlayer();basketballPlayer.sleep(); // 输出: Player is sleeping.basketballPlayer.play();  // 输出: BasketballPlayer is playing basketball.FootballPlayer footballPlayer = new FootballPlayer();footballPlayer.sleep();    // 输出: Player is sleeping.footballPlayer.play();     // 输出: FootballPlayer is playing football.}
}

3.4.2 示例代码2

假设现在有一个文件,里面的内容非常简单,只有一个“Hello World”,现在需要有一个读取器将内容从文件中读取出来,最好能按照大写的方式,或者小写的方式来读。

3.4.2.1 抽象类定义
java">/*** 抽象类,定义了一个读取文件的基础框架,其中 mapFileLine 是一个抽象方法,具体实现需要由子类来完成*/
abstract class BaseFileReader {protected Path filePath; // 定义一个 protected 的 Path 对象,表示读取的文件路径/*** 构造方法,传入读取的文件路径* @param filePath 读取的文件路径*/protected BaseFileReader(Path filePath) {this.filePath = filePath;}/*** 读取文件的方法,返回一个字符串列表* @return 字符串列表,表示文件的内容* @throws IOException 如果文件读取出错,抛出该异常*/public List<String> readFile() throws IOException {return Files.lines(filePath) // 使用 Files 类的 lines 方法,读取文件的每一行.map(this::mapFileLine) // 对每一行应用 mapFileLine 方法,将其转化为指定的格式.collect(Collectors.toList()); // 将处理后的每一行收集到一个字符串列表中,返回}/*** 抽象方法,子类需要实现该方法,将文件中的每一行转化为指定的格式* @param line 文件中的每一行* @return 转化后的字符串*/protected abstract String mapFileLine(String line);
}
  • filePath 为文件路径,使用 protected 修饰,表明该成员变量可以在需要时被子类访问到。

  • readFile() 方法用来读取文件,方法体里面调用了抽象方法 mapFileLine()——需要子类来扩展实现大小写的不同读取方式。

3.4.2.2 子类实现
  • 小写的方式
java">class LowercaseFileReader extends BaseFileReader {protected LowercaseFileReader(Path filePath) {super(filePath);}@Overrideprotected String mapFileLine(String line) {return line.toLowerCase();}
}
  • 大写的方式
java">class UppercaseFileReader extends BaseFileReader {protected UppercaseFileReader(Path filePath) {super(filePath);}@Overrideprotected String mapFileLine(String line) {return line.toUpperCase();}
}

从文件里面一行一行读取内容的代码被子类复用了。与此同时,子类只需要专注于自己该做的工作,LowercaseFileReader 以小写的方式读取文件内容,UppercaseFileReader 以大写的方式读取文件内容。

3.4.2.3 使用抽象类
java">public class FileReaderTest {public static void main(String[] args) throws URISyntaxException, IOException {URL location = FileReaderTest.class.getClassLoader().getResource("helloworld.txt");Path path = Paths.get(location.toURI());BaseFileReader lowercaseFileReader = new LowercaseFileReader(path);BaseFileReader uppercaseFileReader = new UppercaseFileReader(path);System.out.println(lowercaseFileReader.readFile());System.out.println(uppercaseFileReader.readFile());}
}
  • 在项目的 resource 目录下建一个文本文件,名字叫 helloworld.txt,里面的内容就是“Hello World”。

  • 在 resource 目录下的文件可以通过 ClassLoader.getResource() 的方式获取到 URI 路径,然后就可以取到文本内容了。

  • 输出结果如下所示:

java">[hello world]
[HELLO WORLD]

3.5 小结

  • 不能实例化:抽象类不能被实例化。
  • 至少一个抽象方法:抽象类应该至少有一个抽象方法,否则它没有任何意义。
  • 抽象方法无方法体:抽象类中的抽象方法没有方法体。
  • 子类实现抽象方法:抽象类的子类必须给出父类中的抽象方法的具体实现,除非该子类也是抽象类。
    通过抽象类,Java 提供了强大的代码复用和扩展机制,使得代码更加模块化和易于维护。

4 Java 接口

4.1 定义接口

接口通过 interface 关键字来定义,它可以包含常量和方法。接口中的所有变量和方法都会自动添加上 public 修饰符。

java">interface Electronic {// 常量String LED = "LED";// 抽象方法void powerOn();// 静态方法(Java 8 之后)static boolean isEnergyEfficient(String type) {return type.equals("LED");}// 默认方法(Java 8 之后)default void printDescription() {System.out.println("Electronic device");}
}

核心知识点

  • 常量:接口中定义的变量会在编译时自动加上 public static final 修饰符。
  • 抽象方法:没有使用 privatedefaultstatic 关键字修饰的方法是隐式抽象的,编译时会自动加上 public abstract 修饰符。
  • 静态方法:从 Java 8 开始,接口中允许有静态方法。静态方法只能通过接口名调用。
  • 默认方法:从 Java 8 开始,接口中允许定义默认方法。默认方法有方法体,为实现该接口而不覆盖该方法的类提供默认实现。

结论

  • 接口中允许定义变量、抽象方法、静态方法和默认方法。
  • 接口不允许直接实例化。
  • 接口可以是空的。
  • 接口不能使用 final 关键字。
  • 接口的抽象方法不能是 privateprotectedfinal
  • 接口的变量是隐式 public static final

4.2 接口的作用

  • 功能扩展:使某些实现类具有特定功能,如 CloneableComparableComparator
  • 多重继承:Java 只支持单一继承,但通过接口可以实现多重继承。
  • 多态:同一个事件在不同对象上产生不同结果。

示例代码

java">interface Shape {String name();
}class Circle implements Shape {@Overridepublic String name() {return "Circle";}
}class Square implements Shape {@Overridepublic String name() {return "Square";}
}public class Main {public static void main(String[] args) {Shape circleShape = new Circle();Shape squareShape = new Square();System.out.println(circleShape.name()); // 输出: CircleSystem.out.println(squareShape.name()); // 输出: Square}
}

多态存在的 3 个前提

  1. 要有继承关系。
  2. 子类要重写父类的方法。
  3. 父类引用指向子类对象。

4.3 接口的三种模式

  • 策略模式:将每种算法封装到具有共同接口的实现类中,接口设计者可以在不影响调用者的情况下改变算法。
  • 适配器模式:针对调用者的需求对原有接口进行转接。
  • 工厂模式:根据需求生产不同的产品。

4.3.1 策略模式

  • 示例代码
java">// 接口:教练
interface Coach {// 方法:防守void defend();
}// 何塞·穆里尼奥
class Hesai implements Coach {@Overridepublic void defend() {System.out.println("防守赢得冠军");}
}// 德普·瓜迪奥拉
class Guatu implements Coach {@Overridepublic void defend() {System.out.println("进攻就是最好的防守");}
}public class Demo {// 参数为接口public static void defend(Coach coach) {coach.defend();}public static void main(String[] args) {// 为同一个方法传递不同的对象defend(new Hesai());defend(new Guatu());}
}

Demo.defend() 方法可以接受不同风格的 Coach,并根据所传递的参数对象的不同而产生不同的行为,这被称为“策略模式”。

4.3.2 适配器模式

  • 示例代码
java">interface Coach {void defend();void attack();
}// 抽象类实现接口,并置空方法
abstract class AdapterCoach implements Coach {public void defend() {};public void attack() {};
}// 新类继承适配器
class Hesai extends AdapterCoach {public void defend() {System.out.println("防守赢得冠军");}
}public class Demo {public static void main(String[] args) {Coach coach = new Hesai();coach.defend();}
}

Coach 接口中定义了两个方法(defend() 和 attack()),如果类直接实现该接口的话,就需要对两个方法进行实现。

如果我们只需要对其中一个方法进行实现的话,就可以使用一个抽象类作为中间件,即适配器(AdapterCoach),用这个抽象类实现接口,并对抽象类中的方法置空(方法体只有一对花括号),这时候,新类就可以绕过接口,继承抽象类,我们就可以只对需要的方法进行覆盖,而不是接口中的所有方法。

4.3.3 工厂模式

  • 示例代码
java">// 教练
interface Coach {void command();
}// 教练学院
interface CoachFactory {Coach createCoach();
}// A级教练
class ACoach implements Coach {@Overridepublic void command() {System.out.println("我是A级证书教练");}}// A级教练学院
class ACoachFactory implements CoachFactory {@Overridepublic Coach createCoach() {return new ACoach();}}// C级教练
class CCoach implements Coach {@Overridepublic void command() {System.out.println("我是C级证书教练");}}// C级教练学院
class CCoachFactory implements CoachFactory {@Overridepublic Coach createCoach() {return new CCoach();}}public class Demo {public static void create(CoachFactory factory) {factory.createCoach().command();}public static void main(String[] args) {// 对于一支球队来说,需要什么样的教练就去找什么样的学院// 学院会介绍球队对应水平的教练。create(new ACoachFactory());create(new CCoachFactory());}
}

有两个接口,一个是 Coach(教练),可以 command()(指挥球队);另外一个是 CoachFactory(教练学院),能 createCoach()(教出一名优秀的教练)。然后 ACoach 类实现 Coach 接口,ACoachFactory 类实现 CoachFactory 接口;CCoach 类实现 Coach 接口,CCoachFactory 类实现 CoachFactory 接口。当需要 A 级教练时,就去找 A 级教练学院;当需要 C 级教练时,就去找 C 级教练学院。

依次类推,我们还可以用 BCoach 类实现 Coach 接口,BCoachFactory 类实现 CoachFactory 接口,从而不断地丰富教练的梯队。

4.4 抽象类和接口的区别

  • 语法层面

    • 抽象类可以包含具体方法的实现,接口在 Java 8 之前不能。
    • 接口中的成员变量隐式为 public static final,抽象类不是。
    • 一个类可以实现多个接口,但只能继承一个抽象类。
    • 接口是隐式抽象的,不需要使用 abstract 关键字。
    • 接口的方法默认是 public abstract
  • 设计层面

    • 抽象类是对一种事物的抽象,即对类抽象,继承抽象类的子类和抽象类本身是一种 is-a 的关系。
    • 接口是对行为的抽象,接口和类之间并没有很强的关联关系。
    • 抽象类是模板式设计,接口是辐射式设计。

4.5 小结

  • 接口:用于定义一组行为规范,允许实现类具有特定功能,支持多重继承和多态。
  • 抽象类:用于对类进行抽象,提供模板式设计,支持单一继承。

5 思维导图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

6 参考链接

  1. 和妹聊聊Java访问权限修饰符
  2. Java代码初始化块:了解实例初始化和静态初始化的过程
  3. Java抽象类,看这一篇就够了,豁然开朗
  4. Java接口,看这一篇就够了,简单易懂

http://www.ppmy.cn/server/132278.html

相关文章

科研绘图系列:R语言绘制中国地理地图

文章目录 介绍加载R包导入数据图a图b图c图d系统信息介绍 文章提供了绘制图a,图b和图d的数据和代码。该图展示了不同省份的物种分布情况。 加载R包 library(geojsonsf) library(sf) library(ggplot2) library(RColorBrewer) library(ggspatial) library(</

SQL之什么是窗口函数OVER

文章目录 一、OVER 的定义二、OVER 的语法三、OVER 的用法 一、OVER 的定义 OVER 用于为行定义一个窗口&#xff0c;它对一组值进行操作&#xff0c;不需要使用 GROUP BY 子句对数据进行分组&#xff0c;能够在同一行中同时返回基础行的列和聚合列。 二、OVER 的语法 OVER (…

获取京东商品历史价格接口item_history_price介绍

接口开发背景 京东作为中国知名的电商平台&#xff0c;提供了丰富的商品和服务。为了更好地满足用户和商家的需求&#xff0c;京东开放平台推出了多种API接口&#xff0c;其中“item_history_price”接口用于获取指定商品的历史价格信息。这一接口的开发背景在于帮助用户判断当…

2013 lost connection to MySQL server during query

1.问题 使用navicat连接doris&#xff0c;会有这个错误。 2.解决 换低版本的navicat比如navicat11。

鸿蒙进入“无人区”:该如何闯关?

按照华为方面的说法&#xff0c;“打造鸿蒙操作系统是三大战役&#xff0c;目前已经完成了底座和体验两大战役&#xff0c;第三大战役则是生态。”生态固然重要&#xff0c;但要让鸿蒙与当今世界主流操作系统抗衡&#xff0c;乃至成为新一代操作系统中的翘楚&#xff0c;其实还…

Matlab 类方法中没用到类实例对象的情况

背景描述: 自定义一个类&#xff0c;在类方法中&#xff0c;不需要使用类对象的相关属性&#xff0c;如果不将类实例传入参数列表&#xff0c;会报错。 解决方案: 方法一: 把类实例写入参数列表中 classdef MyClassmethods function obj MyClass()% 构造函数: 初始化属性endf…

10- Cesium 中动态处理与两个圆形渐变过渡材质相关的属性

这段代码定义了一个名为 GradientTransitionTwoCircleMaterialProperty 的类,用于处理两个圆形渐变过渡材质的属性。构造函数接受 options 参数,用于设置两个圆的起始和结束颜色及其比例。类包含以下主要部分: 构造函数:初始化 _definitionChanged 事件以及两个圆的起始颜色…

解决React中的Hooks闭包陷阱

React中的Hooks闭包陷阱是一个常见的问题&#xff0c;主要发生在useState和useEffect等Hooks的使用过程中。以下是一些解决React中Hooks闭包陷阱的方法&#xff1a; 一、理解闭包陷阱的成因 useState中的闭包陷阱 useState的参数只会在组件挂载时执行一次&#xff0c;这意味着…