Java面向对象,全程无废话,偏实战

面向对象

定义 / 使用类

// src/Phone.java
public class Phone {// 类属性String brand = "苹果";int price = 7999;// 类方法public void call() {System.out.println("打电话");}public void sendMessage() {System.out.println("发短信");}
}

两个类在同一个包中,那么不需要引入彼此就可以直接使用

// src/Main.java
public class Main {public static void main(String[] args) {// 可以直接使用同一个包中的类Phone p = new Phone();System.out.println(p.brand); // 苹果}
}

成员变量 / 局部变量

  • 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
  • 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
  • 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
  • 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
public class Main {// 这里的是成员变量,也称之为全局变量,定义后不赋值就输出不会报错,因为有默认值// 比如String默认值为null,int为0,boolean为false等等...String name;int age;public static void main(String[] args) {// 这里的是局部变量,定义后不赋值就输出会报错int n;System.out.println(n);}
}

private

被设置了 private 的属性表示私有的,只能在当前类中使用,其他类中不能 直接 使用或赋值

public class Main {public static void main(String[] args) {Phone p = new Phone();System.out.println(p.brand); // 苹果System.out.println(p.price); // 报错,原因私有的属性不能被使用}
}
public class Phone {String brand = "苹果";// 将price属性设置为私有的,只能在当前类中使用private int price = 7999;public void call() {System.out.println("打电话");}public void sendMessage() {System.out.println("发短信");}
}

如果想要改变私有属性的值,可以定义一个方法,通过调用该方法完成设置、获取操作

public class Main {public static void main(String[] args) {Phone p = new Phone();// 调用该方法完成设置操作p.setPrice(8999);}
}
public class Phone {String brand = "苹果";private int price = 7999;public void setPrice(int price) {// 通过this可以指向类里面的属性this.price = price;}
}

this

this 修饰的变量用于指向成员变量,其主要作用是(区分局部变量和成员变量的重名问题)

  • 方法的形参如果与成员变量同名,不带 this 修饰的变量指的是形参,而不是成员变量
  • 方法的形参没有与成员变量同名,不带 this 修饰的变量指的是成员变量
public class Main {public static void main(String[] args) {Phone p = new Phone();p.func("python");}
}
public class Phone {String name = "java";public void func(String name) {// 当与方法形参重名时不加this就是形参nameSystem.out.println(name); // python// 加this表示类属性中的nameSystem.out.println(this.name); // java}
}

当没有形参情况下,不加 this 也可以访问到类属性 name

构造方法

类名 == 构造方法名

public class Main {public static void main(String[] args) {Phone p = new Phone("Java", (byte) 20);}
}
public class Phone {String name;byte age;// 构造方法,等同于js中的constructorpublic Phone(String name, byte age) {System.out.format("%s,%d", name, age);}
}

构造方法的注意事项

  1. 构造方法的创建

    如果没有定义构造方法,系统将给出一个默认的无参数构造方法
    如果定义了构造方法,系统将不再提供默认的构造方法

  2. 构造方法的重载

    如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法

  3. 推荐的使用方式

    无论是否使用,都手工书写无参数构造方法

  4. 重要功能!

    可以使用带参构造,为成员变量进行初始化

class Student {private String name;private int age;public Student() {}public Student(String name) {this.name = name;}public Student(int age) {this.age = age;}public Student(String name, int age) {this.name = name;this.age = age;}
}

标准类

① 类名需要见名知意

② 成员变量使用private修饰

③ 提供至少两个构造方法 (带参与不带参)

④ 提供每一个成员变量对应的 setXxx() / getXxx() 方法

⑤ 如果还有其他行为,也需要写上

public class Main {public static void main(String[] args) {// 带参数// Phone p = new Phone("Java", (byte) 20);// 不带参数Phone p = new Phone();p.setName("Java");p.setAge((byte) 20);p.getName(); // Javap.getAge(); // 20}
}
public class Phone {private String name;private byte age;// 有参数public Phone(String name, byte age) {this.name = name;this.age = age;System.out.println(this.name + " " + this.age);}// 无参数public Phone() {}// 通过set / get来设置或访问类属性的值public void setName(String name) {this.name = name;}public void getName() {System.out.println(this.name);}public void setAge(byte age) {this.age = age;}public void getAge() {System.out.println(this.age);}
}

static

类的静态属性、方法在多次 new 实例后在内存中只会创建一个内存空间,而实例属性在每次 new 时候就会创建一个新的空间

静态成员属于类的,实例成员属于每个实例出来的对象的

静态属性

当类的属性或方法被 static 修饰后,说明这个成员变量是属于类的,它称为类变量或静态成员变量。在使用时通过类名.属性名访问。因为类只有一个,所以静态成员变量在内存中也存在一份,所有的对象都可以使用这个变量

public class Main {public static void main(String[] args) {User u = new User();// 实例也可以访问静态成员,但是不推荐System.out.println(u.data);System.out.println(User.data); // Hello World!}
}
public class User {static String data = "Hello World!";
}

尽管我们使用实例对象来访问静态方法和属性,但实际上编译器会将它们转换为对应的类名进行访问。因此,实例对象和类名都可以用来访问静态方法和属性。

需要注意的是,尽管可以使用实例对象来访问静态方法和属性,但这种做法并不推荐。因为静态方法和属性属于类本身,它们不依赖于实例对象。更好的做法是直接使用类名来访问静态方法和属性,以提高代码的可读性和易于理解

静态成员为什么不能访问实例属性?

因为只有在 new 时候实例才会被创建,如果直接通过类名使用实例的属性,此时 new 并没有创建实例,因此就没有实例属性,所以静态成员不能通过访问实例的属性 / 方法

实例属性

如果没有 static 修饰的成员变量表示实例属性,它是属于每个对象的,必须创建类的对象才可以访问

静态方法

与静态属性同理

public class Main {public static void main(String[] args) {User u = new User();User.func();u.func();}
}
public class User {public static void func(){System.out.println("Hello World!");}
}

需要注意的是静态成员能够被实例访问也可以通过类名访问(不建议),而实例成员只能实例访问,不能通过类名访问。

public class User {String data = "Hello World!";public static void func() {// 被static修饰的称为静态方法,他不能访问实例方法、属性System.out.println(this.data); // 报错}
}

记录实例创建的次数

// Main.java
public class Main {public static void main(String[] args) {new Book();new Book();new Book();new Book();}
}
// Book.java
public class Book {// 记录实例创建的次数static int n = 0;public Book() {n += 1;System.out.printf("实例创建了:%d次\n", n);}
}

注意: 如果把 static 去掉,那么 n 就会每次在 new 实例时候被重新创建然后 0+1 永远是 1 ,所以就需要改成静态属性。因为静态属性一个类只会创建一个内存空间,因此在他每次被 new 创建新对象时并不会被重新创建,而是一直存在于之前的内存中

单例模式

方法一、

public class Main {public static void main(String[] args) {Book b1 = Book.getInstance();Book b2 = Book.getInstance();System.out.println(b1 == b2); // true// Book b3 = new Book(); // 不允许被new实例化}
}
public class Book {private static Book Instance = new Book();// 定义一个空参的构造方法,防止被new实例化private Book() {}public static Book getInstance() {return Instance;}
}

方法二、

public class Main {public static void main(String[] args) {Book book = new Book();System.out.println(book.n); // 1Book a = book.getInstance();a.n = 100;System.out.println(a.n); // 100Book b = book.getInstance();b.n = 1000;System.out.println(b.n); // 1000System.out.println(a.n); // 1000// 可以发现他们的值都是一样的,因为他们都是同一个实例,修改的是同一个实例的值}
}
public class Book {int n = 1;private Book book;public Book getInstance() {// 如果没有实例几局创建一个实例并返回if (book == null) {book = new Book();}// 如果有就直接返回return book;}
}

代码块

public class Student {static String name = "zs";int age = 20;// 静态代码块:随着类的加载而执行,并且只执行一次static {// 只能访问静态属性、方法System.out.format("%s %s\n", "静态代码块", name);}// 构造代码块:每次调用构造方法时,都会执行一次,优先于构造方法执行{// 只能访问实例属性、方法System.out.format("%s %d\n", "构造代码块", age);}
}

静态代码块: 随着类的加载而执行,并且只执行一次,一般用于加载驱动,或者放只需要执行一次的代码

构造代码块: 每次调用构造方法时,都会执行一次,优先于构造方法执行,一般用于统计创建了多少个对象

注意: 静态代码块 优先于 构造代码块执行

继承

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

class 父类 {...
}class 子类 extends 父类 {...
}

注意: Java 是单继承的,一个类只能继承一个直接父类。如果想要实现多继承可以B类继承A类,C类继承B类以此类推…

基本示例

// A.java
public class A {String info = "AAA";
}// B.java
public class B extends A {String info = "BBB";
}// Main.java
public class Main {public static void main(String[] args) {B b = new B();System.out.println(b.info);}
}

应用场景

老师类,学生类,还有班主任类,实际上都是属于人类的,我们可以定义一个人类,把他们相同的属性和行为都定义在人类中,然后继承人类即可,子类特有的属性和行为就定义在子类中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码示例

// Main.java
public class Main {public static void main(String[] args) {// 教师类Teacher teacher = new Teacher();teacher.setName("播仔");teacher.setAge((byte) 31);teacher.setSalary(1000.99);System.out.format("%s %d %f ", teacher.getName(), teacher.getAge(), teacher.getSalary());teacher.teach();// 班主任类BanZhuRen banzhuren = new BanZhuRen();banzhuren.setName("灵涛");banzhuren.setAge((byte) 28);banzhuren.setSalary(1000.99);System.out.format("%s %d %f ", banzhuren.getName(), banzhuren.getAge(), banzhuren.getSalary());banzhuren.admin();// 学生类Student student = new Student();student.setName("播仔");student.setAge((byte) 31);System.out.format("%s %d ", student.getName(), student.getAge());student.behavior();// student.setSalary(1000.99); // student类没有薪水属性,报错!}
}

Human 公共类 将可复用的代码写在这个类中,让其他的类来继承他

// Human.java
public class Human {private String name;private byte age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(byte age) {this.age = age;}
}

Student 类需要用到 Human 中的属性,可以继承它,减少代码冗余,提高代码规范性。不仅如此还可以在类的原有基础上继续扩展,比如扩展一个方法 behavior

// Student.java
public class Student extends Human {public void behavior(){System.out.println("好好学习,天天向上!");}
}

Teacher 如果没有我们想要的属性,也可以自己扩展

// Teacher.java
public class Teacher extends Human {// 扩展一个薪资属性private double salary;public void teach(){System.out.println("老师在认真教技术!");}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}
}
// BanZhuRen.java
public class BanZhuRen extends Human {private double salary ;public void admin(){System.out.println("班主任强调纪律问题!");}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}
}

super

子父类中出现了同名的成员变量时,在子类中需要访问父类中 非私有成员变量 时,需要使用 super 关键字,修饰父类成员变量,类似于之前学过的 this

不同的是 super 代表父类对象的引用,而 this 代表当前对象的引用

public class Main {public static void main(String[] args) {Student s = new Student();}
}
public class Student extends Person {String name = "子类属性";public Student(){// 访问当前子类属性System.out.println(this.name); // 子类属性// 访问父类属性 / 方法System.out.println(super.name); // 父类属性super.func(); // 父类方法}
}
public class Person {String name = "父类属性";public void func() {System.out.println("父类方法");}
}

注意: Person 类中的成员变量是非私有的,子类中可以直接访问。若 Person 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用 private 修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的 getXxx 方法和 setXxx 方法。

类在构造函数中会自动调用 super 从而导致父类的构造函数触发

public class Main {public static void main(String[] args) {Student s = new Student();// 父类被调用了// 子类被调用了}
}
public class Person {public Person(){System.out.println("父类被调用了");}
}
public class Student extends Person {public Student(){System.out.println("子类被调用了");}
}

访问特点

继承中成员变量与方法的访问特点

  1. 如果当前类中属性与变量同名,那么优先输出变量

  2. 如果子类与父类属性重名,那么输出子类的重名属性

  3. 如果想要使用父类的属性,可以通过 super

// Main.java
public class Main {public static void main(String[] args) {Student s = new Student();s.func();}
}
// Person.java
public class Person {int n = 100;public void func(){}
}
// Student.java
public class Student extends Person {int n = 200;public void func() {int n = 300;System.out.println(n); // 300System.out.println(this.n); // 200System.out.println(super.n); // 100}
}

注意: 通过 super 不能访问父类私有的属性

方法重写

当子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

应用场景

子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的方法

例如:我们定义了一个动物类代码如下:

public class Animal  {public void run(){System.out.println("动物跑的很快!");}public void cry(){System.out.println("动物都可以叫~~~");}
}

然后定义一个猫类,猫可能认为父类 cry() 方法不能满足自己的需求

代码示例

public class Cat extends Animal {// 重写cry方法,覆盖父类的public void cry(){System.out.println("喵喵喵!");}
}public class Test {public static void main(String[] args) {// 创建子类对象Cat ddm = new Cat()// 调用父类继承而来的方法ddm.run();// 调用子类重写的方法ddm.cry(); // 喵喵喵!}
}

注意:

  1. 方法重写是发生在子父类之间的关系。

  2. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

  3. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样,否则会导致方法重载。

    public class Main {public static void main(String[] args) {B b = new B();b.info(); # 666b.info(1, 2); # 300}
    }public class A {public void info() {System.out.println(666);}// 被子类重写了public void info(int x, int y) {System.out.println(x + y);}
    }public class B extends A {@Overridepublic void info(int x, int y) {System.out.println(100 + 200);}
    }
    

@Override

在Java继承中,对于方法重写,不是必须要加上 @Override 注解。但是,建议在重写父类方法时使用 @Override 注解,这样可以增加代码的可读性和可维护性。

使用 @Override 注解可以确保方法的正确重写。如果在重写方法时,不小心拼写错误或者方法签名不匹配,编译器会报错。而使用注解,编译器会检查该方法是否确实是重写了父类的方法,如果没有重写成功,编译器会报错,提醒开发者进行修正。

总之,虽然不是强制要求加上 @Override 注解,但是建议在重写父类方法时使用该注解,以增加代码的可读性和可维护性,并避免潜在的错误。

public class Main {public static void main(String[] args) {Student s = new Student();s.func(); // 子类方法}
}
public class Student extends Person {String name = "子类属性";@Overridepublic void func() {System.out.println("子类方法");}
}
public class Person {String name = "父类属性";public void func() {System.out.println("父类方法");}
}

执行顺序

new B() 后他会根据参数来匹配对应的构造方法,如果是空参就会匹配空参的构造,有参就会匹配有参的构造

public class Main {public static void main(String[] args) {new B(); // 1  3new B(100); // 1  4}
}
public class A {public A() {System.out.println(1);}public A(int n) {System.out.println(2);}
}
public class B extends A {public B() {System.out.println(3);}public B(int n) {System.out.println(4);}
}

super 会隐式自动调用

public class Main {public static void main(String[] args) {new B(); // 1  4  3}
}
public class A {public A() {System.out.println(1);}public A(int n) {System.out.println(2);}
}
public class B extends A {public B() {// super(); 这里会隐式调用super然后触发父类的无参构造方法 1// 然后再调用自己的有参构造方法 4this(100);// 最后再打印 3System.out.println(3);}public B(int n) {System.out.println(4);}
}

如果子类调用了父类的构造方法或普通方法,那么他们的 this 就是子类对象

public class Main {public static void main(String[] args) {new B();}
}
public class A {public A() {System.out.println(this); // B@41629346this.info();}public void info() {System.out.println(this); // B@41629346System.out.println("AAA");}
}
public class B extends A {public B() {// super();// 如果子类调用了父类的构造方法或普通方法,那么他们的 `this` 就是子类对象super.info();}public void info() {System.out.println("BBB");}
}
B@41629346
BBB
B@41629346
AAA

权限修饰符

修饰符同一个类中同一个包中不同包的子类不同包的无关类
private
default
protected
public

多态

当一个方法的形参是一个类,我们可以传递这个类所有的子类对象,而且还可以根据不同的对象来调用不同类中的方法。同时多态也是面向对象的三大特性之一

为什么使用多态?

如果没有多态,那么在下图中 register 方法只能传递 Student 学生对象,其他的 Teacheradministrator 对象是无法传递给 register 方法方法的,在这种情况下,只能定义三个不同的 register 方法分别接收学生,老师这两个类

在这里插入图片描述

有了多态之后,方法的形参就可以定义为共同的父类 Person,换句话说只要是继承于 Person 的父类都可以作为 Person 类型传递参数

代码示例

// Main.java
public class Main {public static void main(String[] args) {A b = new B();A c = new C();go(b);go(c);}public static void go(A a){// 传递哪个类就调用哪个类的info方法a.info();}
}
// A.java
public class A {public void info() {System.out.println("AAA");}
}
// B.java
public class B extends A {public void info() {System.out.println("BBB");}
}
// C.java
public class C extends A {public void info() {System.out.println("CCC");}
}
BBB
CCC

注意: 多态必须是以继承的关系才能被定义

弊端

多态编译阶段是看左边父类 类型的,如果子类有些 独有 的功能,那么使用多态就无法访问子类独有功能了

public class Main {public static void main(String[] args) {Person p = new Student();p.setName("张三");p.setAge((byte) 18);// Person类有show方法,即使被Student类重写也是可以的p.show();// 编译报错,编译看左边的Person父类,Person中没有func变量p.func();}
}

转型

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

public class Main {public static void main(String[] args) {// 向上转型  Animal a = new Cat();  a.eat(); // 调用的是 Cat 的 eat// 向下转型:强制转换为子类的类型自然就可以使用子类自己的方法了Cat c = (Cat) a;       c.catchMouse(); // 调用的是 Cat 的 catchMouse}  
}
abstract class Animal {  abstract void eat();  
}  class Cat extends Animal {  public void eat() {  System.out.println("吃鱼");  }  public void catchMouse() {  System.out.println("抓老鼠");  }  
}  class Dog extends Animal {  public void eat() {  System.out.println("吃骨头");  }  public void watchHouse() {  System.out.println("看家");  }  
}

instanceof

为了避免 ClassCastException 的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,注意是引用类型才能使用该关键字

String s = new String("Hello");
System.out.println(s instanceof String); // true

测试

如果变量属于该数据类型或者其子类类型,返回 true,反之 false

public class Main {public static void main(String[] args) {Person p = new Student();p.setName("张三");p.setAge((byte) 18);Student student = (Student) p;student.StudentFunc();// p属于Person或Student类型System.out.println(p instanceof Person); // trueSystem.out.println(p instanceof Student); // trueSystem.out.println(student instanceof Person); // trueSystem.out.println(student instanceof Student); // true// 不属于Teacher类型System.out.println(p instanceof Teacher); // falseSystem.out.println(student instanceof Teacher); // 报错:不兼容的类型: Student无法转换为Teacher// 根据不同的类型转换为不同的类调用不同的方法if (p instanceof Student) {Student s = (Student) p;s.StudentFunc();} else if (p instanceof Teacher) {Teacher t = (Teacher) p;t.TeacherFunc();}}
}

instanceof 新特性

JDK14 的时候提出了新特性,把判断和强转合并成了一行

// 如果p为Student类型 就自动强制转换为Student并赋值给s变量
if (p instanceof Student s) {s.StudentFunc();
} else if (p instanceof Teacher t) {t.TeacherFunc();
}

final

  1. final 修饰的类称为最终类,不能被继承

    public final class A {}public class B extends A {} // 报错:无法从final 'A' 继承
    
  2. 被修饰的方法不能被重写

    public class A {public final void func() {System.out.println(100);}
    }public class B extends A {@Overridepublic final void func() { // 报错:B中的func()无法覆盖A中的func()System.out.println(200);}
    }
    
  3. 被修饰的变量不能被二次赋值

    final int n = 100;
    n = 200; // 报错:无法将值赋给 final 变量 'n'
    
  4. 在类中被修饰的变量称为常量,不能被二次赋值

    public final String KEY = "123123";
    

抽象类

抽象类的特点

public class Main {public static void main(String[] args) {// A a = new A(); 抽象类不能被 new 实例化B b = new B();b.func();b.info();}
}
// 定义抽象类:不能new实例化,只能被继承
public abstract class A {// 定义抽象方法:必须由子类实现public abstract void func();// 抽象类中也可以定义实例方法public void info() {System.out.println("Hello");}
}
public class B extends A {// 实现抽象方法:不然会报错public void func() {System.out.println(100);}
}

注意: 静态属性或方法不能定义为抽象类,并且抽象类中定义的抽象方法必须由子类全部实现,否则就会报错。想要避免报错,要么将子类也设置为抽象类,要么就将抽象类的方法全部实现

//B类继承A类,此时B类也是抽象类,这个时候就可以不重写A类的抽象方法也不会报错
public abstract class B extends A {}

下面是一个简单的抽象类,我们可以定义一个抽象方法 func 然后在 info 方法中调用,当我们继承了 info 的方法后必须实现 func 继承方法才能调用

public class Main {public static void main(String[] args) {B b = new B();b.info();}
}
public abstract class A {public final void info() {System.out.println("开始");func();System.out.println("结束");}public abstract void func();
}
public class B extends A {@Overridepublic void func() {System.out.println("抽象类");}
}

接口类

Java 提供了一个关键字 interface,用来定义接口这种特殊结构

public interface 接口名{//成员变量(默认常量)//成员方法(抽象方法)
}

定义一个简单的接口

public interface A{//这里public static final可以加,可以不加。public static final String NAME = "AAA";//这里的public abstract可以加,可以不加。public abstract void test();
}

使用了接口类,那么就必须重写接口类的全部抽象方法,或者将这个类定义为抽象类

public class Main {public static void main(String[] args) {B b = new B();b.func1(); // func1()b.func2(); // func2()}
}
// 定义接口类
public interface A {void func1();void func2();
}
public class B implements A {// 实现接口类A中的所有方法@Overridepublic void func1() {System.out.println("func1()");}@Overridepublic void func2() {System.out.println("func2()");}
}

多接口

一个类也可以实现多个接口,如下所示定义了 AB 接口,然后由 C 类使用,那么 C 类就必须实现 A、B 接口的所有方法

public class Main {public static void main(String[] args) {C c = new C();c.func1(); // func1c.func2(); // func2// A.NAME = "aaa"; 报错:无法将值赋给 final 变量 'NAME'System.out.println(A.NAME); // AAASystem.out.println(B.NAME); // BBB}
}
// 定义A接口
public interface A {// 默认定义的就是常量,不能被赋值String NAME = "AAA";void func1();
}
// 定义B接口
public interface B{String NAME = "BBB";void func2();
}
// 定义C类,并同时实现A,B接口类
public class C implements A, B {@Overridepublic void func1() {System.out.println("func1");}@Overridepublic void func2() {System.out.println("func2");}
}

应用场景

在这里插入图片描述

首先我们写一个学生类,用来描述学生的相关信息

public class Student {private String name;private char sex;private double score;public Student() {}public Student(String name, char sex, double score) {this.name = name;this.sex = sex;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}
}

接着,写一个StudentOperator接口,表示学生信息管理系统的两个功能。

public interface StudentOperator {void printAllInfo(ArrayList<Student> students);void printAverageScore(ArrayList<Student> students);
}

然后,写一个StudentOperator接口的实现类StudentOperatorImpl1,采用第1套方案对业务进行实现。

public class StudentOperatorImpl1 implements StudentOperator{@Overridepublic void printAllInfo(ArrayList<Student> students) {System.out.println("----------全班全部学生信息如下--------------");for (int i = 0; i < students.size(); i++) {Student s = students.get(i);System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore());}System.out.println("-----------------------------------------");}@Overridepublic void printAverageScore(ArrayList<Student> students) {double allScore = 0.0;for (int i = 0; i < students.size(); i++) {Student s = students.get(i);allScore += s.getScore();}System.out.println("平均分:" + (allScore) / students.size());}
}

接着,再写一个StudentOperator接口的实现类StudentOperatorImpl2,采用第2套方案对业务进行实现。

public class StudentOperatorImpl2 implements StudentOperator{@Overridepublic void printAllInfo(ArrayList<Student> students) {System.out.println("----------全班全部学生信息如下--------------");int count1 = 0;int count2 = 0;for (int i = 0; i < students.size(); i++) {Student s = students.get(i);System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore());if(s.getSex() == '男'){count1++;}else {count2 ++;}}System.out.println("男生人数是:" + count1  + ", 女士人数是:" + count2);System.out.println("班级总人数是:" + students.size());System.out.println("-----------------------------------------");}@Overridepublic void printAverageScore(ArrayList<Student> students) {double allScore = 0.0;double max = students.get(0).getScore();double min = students.get(0).getScore();for (int i = 0; i < students.size(); i++) {Student s = students.get(i);if(s.getScore() > max) max = s.getScore();if(s.getScore() < min) min = s.getScore();allScore += s.getScore();}System.out.println("学生的最高分是:" + max);System.out.println("学生的最低分是:" + min);System.out.println("平均分:" + (allScore - max - min) / (students.size() - 2));}
}

再写一个班级管理类ClassManager,在班级管理类中使用StudentOperator的实现类StudentOperatorImpl1对学生进行操作

public class ClassManager {private ArrayList<Student> students = new ArrayList<>();private StudentOperator studentOperator = new StudentOperatorImpl1();public ClassManager(){students.add(new Student("迪丽热巴", '女', 99));students.add(new Student("古力娜扎", '女', 100));students.add(new Student("马尔扎哈", '男', 80));students.add(new Student("卡尔扎巴", '男', 60));}// 打印全班全部学生的信息public void printInfo(){studentOperator.printAllInfo(students);}// 打印全班全部学生的平均分public void printScore(){studentOperator.printAverageScore(students);}
}

最后,再写一个测试类Test,在测试类中使用ClassMananger完成班级学生信息的管理。

public class Test {public static void main(String[] args) {// 目标:完成班级学生信息管理的案例。ClassManager clazz = new ClassManager();clazz.printInfo();clazz.printScore();}
}

注意:如果想切换班级管理系统的业务功能,随时可以将StudentOperatorImpl1切换为StudentOperatorImpl2。自己试试

新特性

从JDK8开始,接口中新增的三种方法形式。

在普通类中不加修饰符默认是 default 而在接口类中默认是 public

public interface A {// 默认方法:必须使用default修饰,因为默认是publicdefault void a() {System.out.println("默认方法~");}// 私有方法:必须使用private修饰private void b(){System.out.println("私有方法~");}// 静态方法:必须使用static修饰,默认会被public修饰static void c(){System.out.println("静态方法~");}void d();
}
public class B implements A {// 如果不重写就使用默认的,可有可无public void a() {System.out.println("我是重写后的默认方法~");}// 必须实现public void d(){System.out.println("公共方法~");}
}
public class Main {public static void main(String[] args) {B b = new B();b.a(); // 我是重写后的默认方法~~// b.b(); 只能在当前接口中使用A.c(); // 静态方法~b.d(); // 公共方法~}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/134115.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GeoJSON转STL:地形3D打印

我们通过将 GeoJSON 形状坐标提取到点云中并使用 Open3d 应用泊松重建&#xff0c;从 GeoJSON 数据重建 STL 网格。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 我对打印 GeoJSON 山丘的第一次尝试深感不满&#xff0c;因此想出了一个三步流程&#xff0c;仅使用开源…

私域流量的优势

私域流量是指由自身品牌或个人拥有并具备完全掌控权的流量资源。它相比于传统的广告推广&#xff0c;拥有独特的优势。 首先&#xff0c;私域流量能够更加精准地定位目标用户&#xff0c;实现精准传播。不再盲目投放广告&#xff0c;而是通过建立自身社群、粉丝群&#xff0c;获…

HarmonyOS开发:那些开发中常见的问题汇总(一)

前言 本来这篇文章需要讲述静态共享包如何实现远程依赖和上传以及关于静态共享包私服的搭建&#xff0c;非常遗憾的告诉大家&#xff0c;由于组织管理申请迟迟未通过&#xff0c;和部分文档官方权限暂未开放&#xff0c;关于这方面的讲解需要延后了&#xff0c;大概需要等到202…

什么是 JxBrowser

什么是 JxBrowser 文章目录 什么是 JxBrowser如何使用 JxBrowser容易集成支持的平台Java丰富的文档如何运行主要功能值得信赖成熟的专业技术团队及时的支持与帮助参考资料 JxBrowser 是一个商业跨平台 Java 库&#xff0c;可以让您将基于 Chromium 的网页浏览器控件集成到您的 …

基于springboot+vue的药店管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

为什么选择C/C++内存检测工具AddressSanitizer?如何使用AddressSanitizer?

目录 1、C程序中的内存问题 2、AddressSanitizer是什么&#xff1f; 3、AddressSanitizer内存检测原理简述 3.1、内存映射 3.2、插桩 4、为什么选择AddressSanitizer&#xff1f; 4.1、Valgrind介绍 4.2、AddressSanitizer在速度和内存方面为什么明显优于Valgrind 4.3…

无需开通美团外卖会员一日三餐都可天天免费领取美团外卖红包优惠券?

美团外卖红包一天三餐都可用享优惠&#xff1f; 通过草柴公众号&#xff0c;回复美团外卖&#xff0c;天天都可以免费领取一次美团外卖节红包、天天神券1-3个。根据用餐标准早上吃少用3元天天神券、午餐吃饱用7元外卖节红包、晚餐吃好用6元外卖节红包。 *注&#xff1a;每天的…

stm32---定时器输入捕获

一、输入捕获介绍 在定时器中断实验章节中我们介绍了通用定时器具有多种功能&#xff0c;输入捕获就是其中一种。 STM32F1除了基本定时器TIM6和TIM7&#xff0c;其他定时器都具有输入捕获功能 。输入捕获可以对输入的信号的上升沿&#xff0c;下降沿或者双边沿进行捕获&#xf…

VR航天航空巡展VR科技馆航天主题科普设备沉浸遨游太空

每当飞机飞过头顶&#xff0c;我们总是忍不住抬头去仰望。从嫦娥奔月的神话传说&#xff0c;到莱特兄弟实现了上天翱翔的梦想&#xff0c;人类一直在不断探索更辽阔的天空和浩瀚的宇宙。 航空科普 寻梦而行 普乐蛙VR航天航空巡展&#xff0c;正在湖南郴州如火如荼的进行中&…

前端vue3分享——项目封装axios、vite使用env环境变量

文章目录 ⭐前言⭐vue3封装统一的axios请求&#x1f496; 请求拦截器 ⭐vue3使用env环境变量&#x1f496; vite env变量规则&#x1f496; vite.config获取env参数 ⭐总结&#x1f496; 编码sliod原则 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于前端…

C. Manipulating History

Problem - 1688C - Codeforces 思路&#xff1a;因为它给定了最终的串&#xff0c;能够想到能够通过逆操作将整个序列变回去&#xff0c;那我们需要有一个形式str,a,b即在str中将a替换为b&#xff0c;很容易能够看出来&#xff0c;a中的字符串出现了两次&#xff0c;在str中一次…

关于老项目从JDK8升级到JDK17所需要注意的细节

文章目录 ☀️1.关于老项目从JDK8升级到JDK17所需要注意的细节&#x1f338;1.1.更新JDK&#x1f338;1.2.修改Idea中的JDK版本&#x1f338;1.3.关于修改过程中遇到的异常&#x1f338;1.4.IDEA工具栏操作Maven正常&#xff0c;但使用mvn命令运行就报错 ☀️1.关于老项目从JDK…

PCB layout在布线上的设计规范有哪些?

PCB Layout是一项技术活&#xff0c;也是经验活&#xff0c;良好的PCB Layout布线可帮助工程师确保最终的电路板性能、可靠性和制造质量&#xff0c;因此是很多电子工程师的学习重点&#xff0c;下面我们来盘点下PCB Layout关于布线的规范有哪些。 1、地管的引脚接地越短越好&a…

高阶数据结构(2)-----红黑树(未完成)

一)红黑树的基本概念和基本性质: 1)红黑树就是一种高度平衡的二叉搜索树&#xff0c;但是在每一个节点上面都增加了一个存储位来表示结点的颜色&#xff0c;可以是红色或者是黑色&#xff0c;通过对任何一条从根节点到叶子节点上面的路径各个节点着色方式的限制&#xff0c;红黑…

java:逆序排序的三种方法

// 逆序第一种方法 public static void main(String[] args) {int arr[] {11, 22, 33, 44, 55, 66};for (int i arr.length-1; i > 0; i--) {System.out.print("\t"arr[i]);}}缺点&#xff1a;这个是直接逆转&#xff0c;如果里面是随机数没办法比较 逆序第二种…

在ubuntu18.04上编译C++版本jsoncpp/opencv/onnxruntime且如何配置CMakelist把他们用起来~

这篇文章背景是笔者在ubuntu上编译C代码&#xff0c;依赖一些包&#xff0c;然后需要编译并配置到CMakelist做的笔记。主要也是一直不太懂CMakellist&#xff0c;做个笔记以防忘记&#xff0c;也给读者提供一站式的参考&#xff0c;可能您需要的不是这几个包&#xff0c;但大同…

【多区域电力系统模型】三区域电力系统的LQR和模糊逻辑控制(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Python从入门到进阶】35、selenium基本语法学习

接上篇《34、selenium基本概念及安装流程》 上一篇我们介绍了selenium技术的基础概念以及安装和调用的流程&#xff0c;本篇我们来学习selenium的基本语法&#xff0c;包括元素定位以及访问元素信息的操作。 一、元素定位 Selenium元素定位是指通过特定的方法在网页中准确定位…

【教程】IDEA操作GIT

不小心推送代码之后 进行回退 1 找到需要回退的记录 比如要回退13分钟之前提交的代码 选中 右键还原提交 最后再重新推送被还原的提交 就可以了

ArcGIS10.1软件安装教程

ArcGIS10.1中英文&#xff08;32/64位)下载地址&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/1Ksm112WaKMMk6La9ircCng 密码&#xff1a;t70f 安装步骤&#xff1a; 1、我们对安装包进行解压&#xff0c;直接鼠标右击解压即可。 2、 打开我们解压的文件夹&#…