面向对象基础

方法

public、private

  • private 使用 method 来间接赋值
  • SetNum()
  • GetNum()
  • this.x = x;
  • 传入数组类型 (String[] names)

  • 方法可以让外部代码安全地访问实例字段
  • 方法是一组执行语句,并且可以执行任意逻辑
  • 方法内部遇到return返回
  • 外部代码通过public方法操作实例,内部代码通过调用private方法

可变参数

相当于数组类型

class Group {
    private String[] names;
    public void setNames(String... names) {
        this.names = names;
    }
}

Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String

如果把可变参数改为String[]类型,则传参的时候比较麻烦,需要自己构造String[]先。

class Group {
    private String[] names;

    public void setNames(String[] names) {
        this.names = names;
    }
}
Group g = new Group();
g.setNames(new String[] {"Xiao Ming", "Xiao Hong", "Xiao Jun"}); // 传入1个String[]

构造方法

  • 没有构造方法时,编译器会自动生成一个默认构造方法,构造方法名称与类名一样。
  • 如果既要使用带参数的构造方法,又想保留不带参数的构造方法,那么只能把两个构造方法都定义出来
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
        Person p2 = new Person(); // 也可以调用无参数构造方法
    }
}

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 this.name;
    }

    public int getAge() {
        return this.age;
    }
}
  • 其实也可以定义只有一个参数的构造方法,有点重载的感觉,即多构造方法

方法重载

在一个类中,我们可以定义多个方法。如果有一系列方法,它们的功能都是类似的,只有参数有所不同,那么,可以把这一组方法名做成同名方法。

class Hello {
    public void hello() {
        System.out.println("Hello, world!");
    }

    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!");
        } else {
            System.out.println("Hello, " + name + "!");
        }
    }
}

继承

继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。子类自动获得了父类的所有字段,严禁定义与父类重名的字段。

  • Java使用extends关键字来实现继承
  • 超类、父类、基类是同一个,子类、扩展类是同一个
  • Java只允许一个class继承一个类,一个类只有一个父类
  • 子类无法访问父类的private或者private方法
  • 为了让子类可以访问,需要把private改为protected,一个protected字段和方法可以被子类和子类的子类访问
  • 子类引用父类的字段时,可以用super.()
  • 子类不会继承任何父类的构造方法
class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age);
        // 调用父类的构造方法Person(String, int)
        this.score = score;
    }
}

向上转型和向下转型

  • Student是从Person继承下来的,那么一个引用类型为Person的变量,能否只想Student类型的实例
Person p = new Student(); 
  • 把一个子类类型变为父类类型的赋值,称为向上转型。
  • 向上转型实际上是把一个子类变为更抽象的父类,安全。
  • 向上转型时,父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误。
  • 一个父类类型强制转型为子类类型,就是向下转型。
  • 向下转型,恢复了子类的成员变量以及方法。
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

多态

  • override和overload不同的地方。override是重载,如果方法类型和参数不一样就是overload
public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run(); // 应该打印Person.run还是Student.run?
    }
}

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

//打印的是Student.run
  • Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。即调用的是Student的run()方法
  • 多态指针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
  • 在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super来调用
  • 继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override
  • 如果一个类不希望任何其他类继承自它,那么可以把这个类本身标记为final。用final修饰的类不能被继承
  • 可以在构造方法中初始化final字段
class Person {
    public final String name;
    public Person(String name) {
        this.name = name;
    }
}

这样name被赋值后就不能被修改。

抽象类

  • 如果父类方法本身不需要实现任何功能,仅是定义方法签名,目的是让子类去覆写,这就是抽象方法
  • abstract class Person 抽象类
  • 抽象类不能被实例,Persion p = new Person()错误

接口

如果一个抽象类没有字段,所有方法全部是抽象方法,就可以把该抽象类改写为接口interface。

  • 在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
abstract class Person {
    public abstract void run();
    public abstract String getName();
}

interface Person {
    void run();
    String getName();
}
  • 所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。
  • 当一个具体的class去实现一个interface时,需要使用implements关键字
class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}

implements用于接口。在Java中,一个类只能继承另一个类,不能从多个类继承。但是一个类可以实现多个interface。

class Student implements Person, Hello { // 实现了两个interface
    ...
}
  • 一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法
  • 实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

静态字段和静态方法

静态字段static field。静态字段只有一个共享空间,所有变量的静态字段共享一个空间。比如两个对象a,b a.x和b.x是一样的 假设x是static

静态方法

调用静态方法不需要实例变量,通过类名就可以调用。

public class Main {
    public static void main(String[] args) {
        Person.setNumber(99);
        System.out.println(Person.number);
    }
}

class Person {
    public static int number;

    public static void setNumber(int value) {
        number = value;
    }
}

因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。
静态方法常用于工具类,例如Arrays.sort()和Math.random()

接口的静态字段

因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型

public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

classpath和jar

  • classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class
  • jar包就是用来干这个事的,它可以把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件,这样一来,无论是备份,还是发给客户,就简单多了。
  • jar包还可以包含一个特殊的/META-INF/MANIFEST.MF文件,MANIFEST.MF是纯文本,可以指定Main-Class和其它信息。JVM会自动读取这个MANIFEST.MF文件,如果存在Main-Class,我们就不必在命令行指定启动的类名,而是用更方便的命令

模块

  • 如果漏写了某个运行时需要用到的jar,那么在运行期极有可能抛出ClassNotFoundException
  • 从Java 9开始引入的模块,主要是为了解决“依赖”这个问题。如果a.jar必须依赖另一个b.jar才能运行,那我们应该给a.jar加点说明啥的,让程序在编译和运行的时候能自动定位到b.jar,这种自带“依赖关系”的class容器就是模块。
  • 这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们
  • 模块之间的依赖关系已经被写入到模块内的module-info.class文件了。所有的模块都直接或间接地依赖java.base模块,只有java.base模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从Object直接或间接继承而来。

你可能感兴趣的:(面向对象基础)