面向对象

类与对象的关系

类是一种抽象的数据类型,它是对某一类事物整体描述。即抽象的概念。
例如:人、宠物、车,程序中即是Person类、Pet类、Car类
对象是抽象概念的具体实例。即一个体。
例如:张三、狗、奥迪

代码与现实相反,先有类后有对象。
类实例化后会返回一个自己的对象,通过new实例化。

package Demo;
public class Hello {
    public static void main(String[] args) {
        Student student1 = new Student();//创建对象
      // 对象类型  对象名       对象值
        Student student2 = new Student();

    }
    static class Student{//类最多就两点,属性和方法。
        String name;
        int age;
    }
}

对象与引用的关系

引用 "指向" 对象
使用类类型、数组类型、接口类型声明出的变量,都可以指向对象,这种变量就是引用类型变量,简称引用。对象是通过引用来操作的。
在程序中,创建出对象后,直接使用并不方便,所以一般会用一个引用类型的变量去接收这个对象,这个就是所说的引用指向对象。

对象与方法的关系

静态方法

使用static修饰符修饰的方法,就是静态方法。
调用这种方法的时候,"可以"使用对象调用,也"可以"使用类来调用,但是推荐使用类进行调用,因为静态 方法是属于类的。(静态属性也是一样的)

package Demo;
public class Hello {
    public static void main(String[] args) {
        say();//使用static修饰的方法可以直接被调用,如果say方法在同一个包下的其他类中如在Student类中,则Student.say();
    }
    public static void say(){
        System.out.println("Good game");//结果为Good game
    }
}
Student.java
package object;

public class Student {
    public void greet(){
        System.out.println("I am ");
    }
    public static void he(){
        System.out.println("he");
    }
}

demo1.java
package object;

public class demo1 {
    public static void main(String[] args) {
        Student student1=new Student();
        student1.greet();//I am 
        Student.he();//he
        student1.he();//he
    }
}
//虽然static方法he()可以被对象student1调用(idea不会提供快捷操作),但建议使用类Student调用。
而非static方法greet()使用对象student1调用。

非静态方法

没有使用static修饰符修饰的方法,就是非静态方法。
调用这种方法的时候,是"一定"要使用对象的。因为非静态方法是属于对象的。(非静态属性也是一样的)
在项目中方法更多的是非静态的,为了更好的封装,避免直接被外部调用。

package Demo;
public class Hello {
    public static void main(String[] args) {
        Hello s = new Hello();//创建对象,实例化方法所在的类
        s.say();//调用对象中的方法
        //new Hello().say(); 也可以,但更推荐上面那种写法
    }
    public void say(){
        System.out.println("Good game");//结果为Good game
    }
}

同一类中方法调用

假设同一个类中有a、b两个方法

  • a和b都是非静态方法,相互之间可以直接调用。

    public void a(){ 
        b(); 
    }
    public void b(){ 
        
    }
    
  • a和b都是静态方法,相互之间可以直接调用。

    public static void a(){
      b(); 
    }
    public static void b(){ 
    
    }
    
  • a静态方法,b是非静态方法,a方法中不能直接调用b方法,但是b方法中可以直接调用a方法. 静态方法不能调用非静态方法!

    public static void a(){ 
      b();//报错 
    }
    public void b(){
      a(); 
    }
    

值传递和引用传递

调用方法进行传参时,分为值传递和引用传递两种。
如果参数的类型是基本数据类型,那么就是值传递。
如果参数的类型是引用数据类型,那么就是引用传递。
值传递是实参把自己变量本身存的简单数值赋值给形参。
引用传递是实参把自己变量本身存的对象内存地址值赋值给形参。
所以值传递和引用传递本质上是一回事,只不过传递的东西的意义不同而已。

  • 值传递
package Demo;
public class Hello {
    public static void main(String[] args) {
        int a=1;
        System.out.println(a);//1
        change(a);
        System.out.println(a);//1
    }//并没有改变a的值
    public static void change(int x){//往深的说,a和x的内存地址不一样,改变后不影响各自的取值,与void无关
        x=2;
    }
}
  • 引用传递
package Demo;
public class Hello {
    public static void main(String[] args) {
        Student a = new Student();
        System.out.println(a.age);//0
        change(a);
        System.out.println(a.age);//2
    }

    public static void change(Student a) {
        a.age = 2;
    }
}

class  Student{
    int age;//初始值为0
}

构造器

类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。

  • 构造器的特点:
    1.必须和类的名字相同
    2.必须没有返回类型,也不能写void

  • 构造器的作用:
    1.创建对象本质是调用构造器,当调用完构造器后才算new了一个对象。
    2.给对象中的属性初始化赋值。

public class Student{ 
    String name; 
    //构造器
    public Student(){ 
        name = "tom"; 
    } 
}
  • 默认构造器
    初学对象的时候代码中为什么没有构造器?因为在java中,即使我们在编写类的时候没有写构造器,编译之后也会自动添加一个无参构造器,这个无参构造器也被称为默认的构造器。
public class Student{

}//不会在代码中显示,但编译后查看class文件能在Student类中找到
  • 构造器重载
    除了无参构造器之外,很多时候我们还会使用有参构造器,在创建对象时候可以给属性赋值。
    创建有参构造器后系统就不会生成无参构造器了,如果需要用就要自己写无参构造器出来。
    Person.java
package Demo;

public class Person {
    String name;
    public Person(){//定义有参构造器不一定要把无参构造器也写出来
      name="benson";
    }
    public Person(String name){
        this.name=name;//this.name的name指的是Person的属性name,=右边的name指的是参数的name
    }//this关键字指代当前所在类,这里是Person类
}

demo01.java

package Demo;

public class demo01 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);//benson
        Person person1 = new Person("LJS");//输入参数创建对象时会指向有参构造器
        System.out.println(person1.name);//LJS
    }
}

当存在多个有参构造器时,为了提高代码的复用性,可以使用this(参数);来指代对应的有参构造器,节省代码量。
Student.java

public class Student {
    String name;
    int age;
    String address;

    public void study() {
        System.out.println(this.name+"在读书,年龄:"+this.age);
    }

    public Student() {
        name = "student";
        age = 18;
        study();
    }

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

    public Student(String name, int age,String address){
//        this.name = name;
//        this.age = age;
        this(name,age);//替代上面的代码
        this.address = address;
        System.out.print("地址:"+this.address);
    }
}

StudentTest.java

public class StudentTest {
    public static void main(String[] args) {
        Student student = new Student();

        Student student1 = new Student("ben",20);

        Student student2 = new Student("benson",22,"广东");
    }
}

内存分析


JDK8后方法区从堆中改成存到本地内存中


封装

制造厂家为了方便我们使用电视,把复杂的内部细节全部封装起来,只给我们暴露简单的接口,比如:电源开关。
需要让用户知道的暴露出来,不需要让用户了解的全部隐藏起来。这就是封装。
封装是把一个类的各种属性和方法集成起来

封装的步骤

    1. 使用private修饰需要封装的成员变量。
    1. 提供一个公开的方法设置或者访问私有的属性
      设置 通过set方法,命名格式: set属性名(); 属性的首字母要大写
      访问 通过get方法,命名格式: get属性名(); 属性的首字母要大写
//对象能在类的外部"直接"访问 

public class Student{ 
​		public String name; 
​		public void println(){ 
​			System.out.println(this.name); 
​		} 
}
public class Test{ 
​		public static void main(String[] args){ 
​			Student s = new Student(); 
​			s.name = "tom"; 
​		} 
} 

在类中一般不会把数据直接暴露在外部的,而使用private(私有)关键字把数据隐藏起来

public class Student{ 
    private String name; 
}
public class Test{ 
    public static void main(String[] args){ 
        Student s = new Student(); //编译报错,在类的外部不能直接访问类中的私有成员 
        s.name = "tom"; 
    } 
}

如果在类的外部需要访问这些私有属性,那么可以在类中提供对于的get和set方法,以便让用户在类的外部可以间接的访问到私有属性

//set负责给属性赋值 
//get负责返回属性的值 
public class Student{ 
    private String name; 
    public void setName(String name){ 
        this.name = name; 
    }
    public String getName(){ 
        return this.name; 
    }
}
public class Test{ 
    public static void main(String[] args){ 
        Student s = new Student(); 
        s.setName("tom"); 
        System.out.println(s.getName());
    }
}

继承

  • 继承的本质在于抽象。类是对对象的抽象,继承是对某一批类的抽象。
  • 继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
    继承的意义在于减少有共性的类与类之间的代码重复

    子类可以使用直接或间接父类的内容,但不能使用“叔叔辈”的内容
public class Student extends Person{ //Student是子类,Person是父类,student is a Person

}
  • JAVA中类只有单继承,没有多继承,即除了object类,一个子类只能继承一个父类。ps:接口可以多继承
  • 父类中的属性和方法可以被子类继承
    父类中的属性和方法使用public修饰,在子类中继承后"可以直接"使用
    父类中的属性和方法使用private修饰,在子类中继承后"不可以直接"使用,父类要提供get/set方法
    父类中的构造器是不能被子类继承的,但是子类的构造器中,会隐式的调用父类中的无参构造器(默认使用super关键字)
    用final修饰的类不能被继承,没有子类。final断子绝孙!

Object类

java中的每一个类都是"直接" 或者 "间接"的继承了Object类.所以每一个对象都和Object类有"is a"的关系。
从API文档中,可以看到任何一个类最上层的父类都是Object。(Object类本身除外)AnyClass is aObject。

Super关键字

子类继承父类之后,在子类中可以使用this来表示访问或调用子类中的属性或方法,使用super就表示访问或调用父类中的属性和方法。

  • 访问父类中的属性
  • 调用父类中的方法
  • 调用父类的构造器
    子类可能会用到父类的数据,所以会先调用父类的无参构造器以得到父类初始化的数据
    • 父类无构造器
    • 显式地调用父类的有参构造器

super使用的注意的地方

  1. 用super调用父类构造方法,必须是构造方法中的第一个语句。
  2. super只能出现在子类的方法或者构造方法中。
  3. super 和 this 不能够同时调用构造方法。(因为this也是在构造方法的第一个语句)
Person.java
package object.extendsDemo.constrctorDemo;

public class Person {
    public String name;
    public int age;

    public Person(){
        System.out.println("父类无参构造");
    }
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
}

Student.java
package object.extendsDemo.constrctorDemo;

public class Student extends Person{
    public Student(){
        //隐式调用父类无参构造器
        //super();可以写出来,但必须在第一行
        System.out.println("子类无参构造");
    }
    public Student(String name,int age){
        super(name,age);
    }
}

StudentTest.java
package object.extendsDemo.constrctorDemo;

public class StudentTest {
    public static void main(String[] args) {
        Student s=new Student();
        Student student=new Student("Benson",18);
        System.out.println(student.name+":"+student.age);
    }
}

方法重写override

涉及多态,理解就好
静态方法是类的方法,有static时因为b是B类定义的,所以b调用B类的方法,即有static看=左边

非静态方法是对象的方法,没有static时b调用的是对象的方法,因为b是A类new出来的对象,所以b调用A的方法
无static时看=右边

重写的前提是继承,子类重写父类的方法。只针对类的方法,与属性无关。非静态方法才算重写。private方法不能重写
而重载是在同一个类中,方法名相同,参数列表不同。

重写语法

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 访问控制修饰符可以被扩大,但是不能被缩小: public>protected>default>private,如protected->public
  4. 抛出异常类型的范围可以被缩小,但是不能被扩大(本来欠1k,创建子类后不能欠1w,可以欠100)
    ClassNotFoundException<Exception
  5. 返回类型可以相同,也可以不同,如果不同的话,子类重写后的方法返回类型必须是父类方法返回类型的子类型
    例如:父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型
  • 权限修饰符

为什么要重写?
子类继承父类,继承了父类中的方法,但是父类中的方法并不一定能满足子类中的功能需要,所以子类中需要把方法进行重写。

重写也可以理解成子类的方法覆盖了父类同名的方法

Dog.java
package object.dogs;

public class Dog {
    public void eat(){
        System.out.println("吃狗粮");
    }
    public void drink(){
        System.out.println("喝水");
    }
    public void lookhome(){
        System.out.println("看家");
    }
}

Husky.java
package object.dogs;

public class Husky extends Dog{
    public void breakhome(){
        System.out.println("拆家");
    }
}

SharPei.java
package object.dogs;

public class SharPei extends Dog{
    @Override
    public void eat() {
        super.eat();
        System.out.println("吃骨头");
    }
}

ChineseDog.java
package object.dogs;

public class ChineseDog extends Dog{
    @Override
    public void eat() {
        System.out.println("吃剩饭");
    }
}

DogTest.java
package object.dogs;

public class DogTest {
    public static void main(String[] args) {
        Husky husky=new Husky();
        SharPei sharpei=new SharPei();
        ChineseDog chineseDog=new ChineseDog();

        husky.eat();
        husky.drink();
        husky.lookhome();
        husky.breakhome();

        sharpei.eat();

        chineseDog.eat();
    }
}

Employee.java
package object.extendsDemo.test;

public class Employee {
    private int id;
    private String name;
    private double salary;

    //构造器
    public Employee(){

    }
    public Employee(int id,String name,double salary){
        this.id=id;
        this.name=name;
        this.salary=salary;
    }

    //get/set
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    //方法
    public void work(){
        System.out.println("工作");
    }
    public void eat(){
        System.out.println("吃米饭");
    }
}

Manager.java
package object.extendsDemo.test;

public class Manager extends Employee{
    private double bonus;

    public Manager(){

    }
    public Manager(int id,String name,double salary,double bonus){
        super(id,name,salary);
        this.bonus=bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public void work(){
        System.out.println("管理其他人");
    }
}

Cook.java
package object.extendsDemo.test;

public class Cook extends Employee{
    public Cook(){

    }
    public Cook(int id,String name,double salary){
        super(id,name,salary);
    }

    @Override
    public void work() {
        System.out.println("炒菜");
    }
}

EmployeeTest.java
package object.extendsDemo.test;

public class EmployeeTest {
    public static void main(String[] args) {
        Manager manager=new Manager(1,"mam",10000,5000);
        System.out.println(manager.getSalary());
        manager.work();

        Cook cook=new Cook(2,"cook",8000);
        cook.work();
        System.out.println(cook.getSalary());
    }
}

多态

多态即对象的多种形态

  • 多态的应用场景:

    有三种人注册账号,在代码中要怎么定义注册方法的形参呢?

    不可能给每种人都写一遍同样的方法,因为程序的延展性不好,如果多了个辅导员的角色,那么又要再写一次注册的方法

    此时可以用多态解决问题,让学生老师管理员的共同父类Person来当参数,这样就可以传入什么对象就使用相应对象的方法

    传入学生的对象,就会使用学生的注册方法

多态的表现形式

父类类型 对象名=子类对象;
//即父类引用指向子类对象

多态的前提

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

Person.java
package object.polymorphism;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show(){
        System.out.println(name+","+age);
    }
}

Student.java
package object.polymorphism;

public class Student extends Person{
    @Override
    public void show() {
        System.out.println("学生的信息为:"+getName()+","+getAge());
    }
}

Teacher.java
package object.polymorphism;

public class Teacher extends Person{
    @Override
    public void show() {
        System.out.println("教师的信息为:"+getName()+","+getAge());
    }
}

Administrator.java
package object.polymorphism;

public class Administrator extends Person{
    @Override
    public void show() {
        System.out.println("管理员的信息为:"+getName()+","+getAge());
    }
}

Test.java
package object.polymorphism;

public class Test {
    public static void main(String[] args) {
        Student s=new Student();
        s.setName("学生");
        s.setAge(18);

        Teacher t=new Teacher();
        t.setName("老师");
        t.setAge(30);

        Administrator a=new Administrator();
        a.setName("管理员");
        a.setAge(33);

        register(s);
        register(t);
        register(a);
    }
    public static void register(Person p){
        //要求这个方法既能接收学生,又能接收老师,也能接受管理员
        //可以把参数写成三者的父类
        p.show();
    }
}

多态的好处

使用父类型的对象作为参数,可以接收所有子类对象
加强代码的扩展性

多态的特点

  • 调用成员变量的特点
    编译看左边,运行也看左边
  • 调用成员方法的特点
    编译看左边,运行看右边
Animal.java
package object.polymorphism.t2;

public class Animal {
    String name="动物";
    public void show(){
        System.out.println("Animal-show");
    }
}

Cat.java
package object.polymorphism.t2;

public class Cat extends Animal{
    String name="猫";

    @Override
    public void show() {
        System.out.println("Cat-show");
    }
}

Dog.java
package object.polymorphism.t2;

public class Dog extends Animal{
    String name="狗";

    @Override
    public void show() {
        System.out.println("Dog-show");
    }
}

demo.java
package object.polymorphism.t2;

public class demo {
    public static void main(String[] args) {
        Animal a=new Cat();
        //调用成员变量:编译看左边,运行也看左边的Animal类型的a
        System.out.println(a.name);//动物

        //调用成员方法:编译看左边,运行看右边 走Cat类的方法
        a.show();//Cat-show
    }
}

多态的缺点

不能调用子类特有的功能

//把上面的Cat类加多个方法
package object.polymorphism.t2;

public class Cat extends Animal{
    String name="猫";

    @Override
    public void show() {
        System.out.println("Cat-show");
    }

    public void catchMouse(){
        System.out.println("抓老鼠");
    }
}

回到demo.java,发现a没有Cat特有的catMouse方法

因为调用成员方法编译看左边,左边的Animal类中并没有catMouse方法,所以报错。
解决方法:
把a从Animal类型转回Cat类型,大转小,与强制转换类型相似

package object.polymorphism.t2;

public class demo {
    public static void main(String[] args) {
        Animal a=new Cat();
        //调用成员变量:编译看左边,运行也看左边的Animal类型的a
        System.out.println(a.name);//动物

        //调用成员方法:编译看左边,运行看右边 走Cat类的方法
        a.show();//Cat-show
        if (a instanceof Cat){//不能乱转换,用instanceof判断对象是不是某某类型或者某某类型的子类
            Cat b=(Cat)a;
            b.catchMouse();
        }else if (a instanceof Dog){
            Dog d=(Dog) a;
        }

    }
}

代码块

局部代码块

局部:方法里面 代码块:大括号 局部代码块:写在方法里面的大括号
作用:提前结束变量的生命周期以节约内存(现在已经用不上了)

int b=2;写在局部代码块中,那么变量b就是局部变量
且它将会在局部代码块之后(第11行)在内存中消失,所以打印b会报错

构造代码块

写在类中成员位置的代码块
作用:提取构造器中重复的代码

demo3.java与demo2.java相比,将无参和有参构造器中相同的语句放到了构造代码块中
而且在创建对象时构造代码块会优先于构造方法执行

demo3.java
package object.codeblock;

public class demo3 {
    private String name;
    private int age;

    {
        System.out.println("name,age");
    }

    public demo3(){
        System.out.println("空参");
    }

    public demo3(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("有参");
    }
}

demo3test.java
package object.codeblock;

public class demo3 {
    private String name;
    private int age;

    {
        System.out.println("name,age");
    }

    public demo3(){
        System.out.println("空参");
    }

    public demo3(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("有参");
    }
}


但这种代码块也渐渐被淘汰了,因为如果有第三个构造器没有与前两个有相同的语句,那么在调用第三个构造器时还是会触发前两个构造器的重复语句
可以在无参中用this解决,也可以把重复语句放到一个函数里,用到时再调用函数。

静态代码块

通过static关键字修饰,随着类的加载而加载并且自动触发、只执行一次
构造代码块是每创建一个对象就会执行一次
作用:在类加载的时候,需要数据初始化时使用
比如学生信息管理系统中可以提前添加些学生信息进去


第二幅图也可以达到静态代码块的效果,它是写在main方法里的,但有弊端。
因为只要是方法就可能会被反复调用,造成图中的信息被反复添加。

静态导入包
静态导包就是java包的静态导入,用import static代替import,导入这个类里的静态方法。
好处:这种方法的好处就是可以简化一些操作
例如打印操作System.out.println(…);就可以将其写入一个静态方法print(…),在使用时直接print(…)就可以了。
但是这种方法建议在有很多重复调用的时候使用,如果仅有一到两次调用,不如直接写来的方便。

import static java.lang.Math.random; 
import static java.lang.Math.PI; 
public class Test { 
    public static void main(String[] args) { 
        //之前是需要Math.random()调用的,即System.out.println(Math.random());
        System.out.println(random()); 
        System.out.println(PI); 
    } 
}

抽象类abstract


父类的work()方法描述的比较笼统,子类Student和Teacher应当要重写,但难免会出现子类忘了重写或者就不重写的情况
这时可以用抽象类来规避这种错误,也方便了描述

用abstract修饰成抽象方法,抽象方法没有方法体,子类必须重写,抽象方法所在的类必须声明为抽象类。

  • 抽象类不能创建对象(实例化)
    反过来想,如果抽象类能创建对象,那它的对象调用抽象方法,但抽象方法没有方法体,调用了寂寞,所以抽象类不能实例化。
  • 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
  • 抽象类可以有构造方法
    抽象类不能被实例化,那为什么还可以有构造方法呢?
    作用:当创建子类对象时给子类对象的属性赋值
  • 抽象类的子类要么重写所有抽象方法,要么也是抽象类
    意义:
    抽象类更多用于团队开发,比如规范某一方法如eat方法的重写。方便团队成员对eat方法的重写,而不用找其他成员的对eat方法是怎么描述的

接口interface

接口就是一种规则

兔子不会游泳,父类Animal就不能有游泳swim方法。但又不适合狗和青蛙各写一个swim方法,因为可能不规范
而且如果以后还有其他子类也会游泳那又要写多一遍swim方法。此时就可以用接口来定义游泳的规则
与抽象类相比,接口就显得没有跟子类没有太紧密的联系,
接口只是规则,只要能游泳的都能用,机器狗也行

  • 接口不能实例化
  • 接口的子类(实现类)要么重写接口所有的抽象方法,要么是抽象类
  • 接口和类的关系可以是单实现,也可以是多实现
public class 类名 implements 接口名1,接口名2{}
  • 实现类可以继承一个类的同时实现多接口
public class 类名 extends 父类 implements 接口1,接口2{}

Animal.java
package object.interfaces;

public abstract class Animal {
    private String name;
    private int age;

    public Animal(){}
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }

    public String getName(){
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}

Swim.java
package object.interfaces;

public interface Swim {
    public abstract void swim();
}

Frog.java
package object.interfaces;

public class Frog extends Animal implements Swim{
    public Frog() {
    }

    public Frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("青蛙在吃虫子");
    }

    @Override
    public void swim() {
        System.out.println("蛙泳");
    }
}

Rabbit.java
package object.interfaces;

public class Rabbit extends Animal{
    public Rabbit() {
    }

    public Rabbit(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("兔子在吃胡萝卜");
    }
}

Test.java
package object.interfaces;

public class Test {
    public static void main(String[] args) {
        Frog f=new Frog("Frog",7);
        System.out.println(f.getName()+","+f.getAge());
        f.eat();
        f.swim();

        Dog d=new Dog();
        d.eat();
        d.swim();

        Rabbit r=new Rabbit();
        r.eat();
    }
}

接口成员的特点



实现多接口的情况下如果有同名的方法,那么只需要重写一次即可。
实现有继承关系的接口时要重写它包括父接口的所有抽象方法


Person.java
package object.interfaces.i3;

public abstract class Person {
    private String name;
    private int age;

    public Person(){}
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }

    public String getName(){return name;}

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Athlete.java
package object.interfaces.i3;

public abstract class Athlete extends Person{
    public Athlete() {
    }

    public Athlete(String name, int age) {
        super(name, age);
    }

    public abstract void Study();
}

Coach.java
package object.interfaces.i3;

public abstract class Coach extends Person{
    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    public abstract void teach();
}

English.java
package object.interfaces.i3;

public interface English {
    public abstract void speakEnglish();
}

PingPongAthlete.java
package object.interfaces.i3;

public class PingPongAthlete extends Athlete implements English{
    public PingPongAthlete() {
    }

    public PingPongAthlete(String name, int age) {
        super(name, age);
    }

    @Override
    public void Study() {
        System.out.println(this.getName()+"在学打乒乓球");
    }

    @Override
    public void speakEnglish() {
        System.out.println(this.getName()+"在说英语");
    }
}

PingpongCoach.java
package object.interfaces.i3;

public class PingpongCoach extends Coach implements English{
    public PingpongCoach() {
    }

    public PingpongCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println(this.getName()+"在教打乒乓球");
    }

    @Override
    public void speakEnglish() {
        System.out.println(this.getName()+"在说英语");
    }
}

BasketballAthlete.java
package object.interfaces.i3;

public class BasketballAthlete extends Athlete{
    public BasketballAthlete() {
    }

    public BasketballAthlete(String name, int age) {
        super(name, age);
    }

    @Override
    public void Study() {
        System.out.println(this.getName()+"在学打篮球");
    }
}

BasketballCoach.java
package object.interfaces.i3;

public class BasketballCoach extends Coach{
    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println(this.getName()+"在教打篮球");
    }
}

Test.java
package object.interfaces.i3;

public class Test {
    public static void main(String[] args) {
        PingPongAthlete ppa=new PingPongAthlete("马龙",27);
        System.out.println("乒乓球运动员:"+ppa.getName()+","+ppa.getAge());
        ppa.Study();
        ppa.speakEnglish();

        BasketballAthlete bba=new BasketballAthlete("郭艾伦",26);
        System.out.println("篮球运动员:"+bba.getName()+","+bba.getAge());
        bba.Study();

        PingpongCoach ppc=new PingpongCoach("刘国梁",45);
        System.out.println("乒乓球教练:"+ppc.getName()+","+ppc.getAge());
        ppc.teach();
        ppc.speakEnglish();

        BasketballCoach bbc=new BasketballCoach("姚明",36);
        System.out.println("篮球教练:"+bbc.getName()+","+bbc.getAge());
        bbc.teach();
    }
}

接口的默认方法default

当项目版本更新后需要在接口那新增10个功能(接口升级),但如果往接口中新增抽象方法,那它的实现类就会报错
要怎么在接口升级的同时又不会使其实现类报错呢?
这里就用到了JDK8之后的特性,允许接口中的方法有方法体,这种方法用default修饰,且不能省略
子类不一定要重写default方法,但重写时要把default修饰词去掉
实现多接口时若存在相同名字的默认方法,则要重写默认方法

因为interImpl类在多实现接口inter1和2时,两接口都有同名的默认方法,但interImpl没有重写,所以报错

接口的静态方法


接口的静态方法不需要也不能重写,可以被测试类直接调用

接口的私有方法

接口中两个默认方法中有重复的代码,可以将它们提取到方法log中,然后默认方法再调用log方法

如果log方法中记录的是不适合分享的数据,可以把log变成私有方法private
如果默认方法改成静态方法,则把log变成私有静态方法private static



接口多态


USB.java

public interface USB {
    public void work();//接口的方法是抽象的,可以不用abstract修饰
}

Mouse.java

public class Mouse implements USB{
    @Override
    public void work(){
        System.out.println("鼠标已接入USB");
    }
}

Keyboard.java

public class Keyboard implements USB{
    @Override
    public void work() {
        System.out.println("键盘已接入USB");
    }
}

Computer.java

public class Computer {
    public void connect(USB usb){
        usb.work();
    }
}

ComputerTest.java

public class ComputerTest {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Mouse mouse = new Mouse();
        Keyboard keyboard = new Keyboard();

        computer.connect(mouse);//鼠标已接入USB
        computer.connect(keyboard);//键盘已接入USB
    }
}

适配器模式

当一个接口抽象类方法比较多时,但我写一个类时又只需要它的一部分方法,此时可以新建一个空实现类来作为接口的适配器
然后再让要写的类继承适配器即可,这样一来我想用哪个方法就用哪个方法

内部类

类的五大成员:属性、方法、构造器、代码块、内部类
内部类:在一个类的里面再定义一个类
在Outer类里定义Inner类,Inner就是内部类,Outer就是外部类
与这两类都无关的就叫外部其他类

应用场景:
汽车的发动机、ArrayList的迭代器、人的心脏

一般会这么写

但发动机是一个独立的个体,跟车本身还是有区别的
可以把发动机的属性放到一个类里,但单纯只是一个普通类的话,在题意的背景下,发动机需要依赖车存在,没什么实际意义
这时就可以把发动机的类定义为内部类

内部类表示的事物是外部类的一部分,内部类单独出现通常没有意义

内部类的特点

  • 内部类可以直接访问外部类的成员,包括私有的
  • 外部类要访问内部类的成员,要创建对象
package object.InnerClass.t1;

public class Car {
    public String carName;
    public int carAge;
    public String carColor;

    public void show(){
        Engine e=new Engine();//需要创建对象才能访问内部类成员
        System.out.println(carAge+","+e.engineAge);
    }

    class Engine{
        public String engineName;
        public int engineAge;

        public void show(){
            System.out.println(engineName+","+carName);
            //可以直接访问外部类的成员
        }
    }

}

成员内部类


一般成员内部类不用static修饰,成员内部类的地位与成员变量相同

静态内部类

静态内部类是一种特殊的成员内部类


局部内部类

匿名内部类




真正的没有名字的类是紫色圈圈那一块,与Swim()是实现关系,new的是紫色圆圈圈出来的没名字的类的对象
书写格式的整体叫做匿名内部类的对象更为贴切

  • 应用场景


    匿名内部类可以是成员内部类(如图),也可以是局部内部类。

练习
Role.java

package object.pk;

import java.util.Random;

public class Role {
    private String name;
    private int hp;
    private int atk;
    private String character;
    private String skill;

    public Role() {

    }

    public Role(String name) {
        this.name = name;
        hp = 100;
        atk = 10;
        setCharacter(index_chara);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setHp(int hp) {
        this.hp = hp;
    }

    public int getHp() {
        return hp;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    String[] characters = {"开朗", "沉稳", "浮躁", "胆小"};
    Random r_character = new Random();
    int index_chara = r_character.nextInt(characters.length);//随机数为0~characters.length-1的下标

    public String getCharacter() {
        return character;
    }
    public void setCharacter(int index_chara) {
        this.character = characters[index_chara];
        switch (this.getCharacter()) {
            case "开朗":
                this.setHp(120);
                this.setAtk(20);
                break;
            case "沉稳":
                this.setHp(130);
                break;
            case "浮躁":
                this.setHp(90);
                this.setAtk(25);
                break;
            case "胆小":
                this.setHp(140);
                this.setAtk(5);
                break;
        }
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    private int ap;
    String[] skills_pikachuu = {"电光火石", "钢铁摆尾", "十万伏特"};
    String[] skills_firedradon = {"火球", "疯狂乱抓", "火焰喷射"};

    public void Skills(String name,int x) {
        if (name == "皮卡丘") {
            this.skill = skills_pikachuu[x];
            switch (skills_pikachuu[x]) {
                case "电光火石":
                    this.setAp(10);
                    break;
                case "钢铁摆尾":
                    this.setAp(20);
                    break;
                case "十万伏特":
                    this.setAp(30);
                    break;
            }
        } else if (name == "小火龙") {
            this.skill = skills_firedradon[x];
            switch (skills_firedradon[x]) {
                case "火球":
                    this.setAp(10);
                    break;
                case "疯狂乱抓":
                    this.setAp(20);
                    break;
                case "火焰喷射":
                    this.setAp(30);
                    break;
            }
        }
    }

    public int getAp() {
        return ap;
    }
    public void setAp(int ap) {
        this.ap = ap;
    }

    public void attack (Role role){
            Random r_skill = new Random();
            int index_skill = r_skill.nextInt(skills_pikachuu.length);
            Skills(this.getName(),index_skill);
            int damage = this.getAtk() +this.getAp();
            int remainHp = role.getHp() - damage;
            remainHp = remainHp < 0 ? 0 : remainHp;//剩余血量不小于0
            role.setHp(remainHp);
            System.out.println(this.getName() + "对" + role.getName() + "使用"+this.skill+",造成了" + damage + "点伤害,"
                    + role.getName() + "剩余" + remainHp + "点hp。");
        }
    }

GameTest.java

package object.pk;

public class GameTest {
    public static void main(String[] args) {
        Role r1=new Role("皮卡丘");
        Role r2=new Role("小火龙");

        System.out.println("皮卡丘VS小火龙");
        System.out.println("皮卡丘:");
        System.out.println("性格:"+r1.getCharacter());
        System.out.println("HP:"+r1.getHp());
        System.out.println("攻击力:"+r1.getAtk());
        System.out.println("=============================");
        System.out.println("小火龙:");
        System.out.println("性格:"+r2.getCharacter());
        System.out.println("HP:"+r2.getHp());
        System.out.println("攻击力:"+r2.getAtk());

        System.out.println("===========比赛开始===========");


        while (true){
            r1.attack(r2);
            if (r2.getHp()==0){
                System.out.println(r1.getName()+"打败了"+r2.getName()+"!");
                break;
            }

            r2.attack(r1);
            if (r1.getHp()==0){
                System.out.println(r2.getName()+"打败了"+r1.getName()+"!");
                break;
            }
        }
    }
}

各种类的意义


posted @ 2022-04-04 23:20  ben10044  阅读(25)  评论(0编辑  收藏  举报