方法
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直接或间接继承而来。