Open yangsirgo opened 3 years ago
class Person {
public void run() {
System.out.println("Person.run");
}
}
在子类Student中,覆写这个run()方法:
class Student extends Person { @Override public void run() { System.out.println("Student.run"); } }
- Java的方法调用总是作用于运行期对象的实际类型,这种行为称为多态;
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。例如
public void runTwice(Person p) { p.run(); p.run(); }
它传入的参数类型是Person,我们是无法知道传入的参数实际类型究竟是Person,还是Student,还是Person的其他子类,因此,也无法确定调用的是不是Person类定义的run()方法。
多态的特性就是,运行期才能动态决定调用的子类方法。
多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
- final修饰符有多种作用:
1. final修饰的方法可以阻止被覆写;
class Person { protected String name; public final String hello() { return "Hello, " + name; } }
Student extends Person { // compile error: 不允许覆写 @Override public String hello() { } }
2. final修饰的class可以阻止被继承;
final class Person { protected String name; }
// compile error: 不允许继承自Person Student extends Person { }
3. final修饰的field必须在创建对象时初始化,随后不可修改。
class Person { public final String name = "Unamed"; }
Person p = new Person(); p.name = "New Name"; // compile error! //对final字段重新赋值会报错:
//可以在构造方法中初始化final字段:
class Person { public final String name; public Person(String name) { this.name = name; } }
//这种方法更为常用,因为可以保证实例一旦创建,其final字段就不可修改。
class Person {
public abstract void run();
}
把一个方法声明为abstract,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以,Person类也无法被实例化。必须把Person类本身也声明为abstract,才能正确编译它:
abstract class Person {
public abstract void run();
}
无法实例化的抽象类有什么用?
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
例如,Person类定义了抽象方法run(),那么,在实现子类Student的时候,就必须覆写run()方法:
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run();
}
}
abstract class Person {
public abstract void run();
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
面向抽象编程的本质就是:
上层代码只定义规范(例如:abstract class Person);
不需要子类就可以实现业务逻辑(正常编译);
具体的业务逻辑由不同的子类实现,调用者并不关心。
通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;
定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;
如果不实现抽象方法,则该子类仍是一个抽象类;
面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。
abstract class Person {
public abstract void run();
public abstract String getName();
}
就可以把该抽象类改写为接口:interface。 使用interface可以声明一个接口:
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;
}
}
我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface,例如:
class Student implements Person, Hello { // 实现了两个interface
...
}
接口继承: 一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
此时,Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。 接口也是数据类型,适用于向上转型和向下转型;
接口的所有方法都是抽象方法,接口不能定义实例字段;
接口可以定义default方法(JDK>=1.8)。 实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。
public class Main {
public static void main(String[] args) {
Person p = new Student("Xiao Ming");
p.run();
}
}
interface Person {
String getName();
default void run() { //在接口中,可以定义default方法。例如,把Person接口的run()方法改为default方法:
System.out.println(getName() + " run");
}
}
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
class Person {
public String name;
public int age;
// 定义静态字段number:
public static int number;
}
用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变量,也无法访问实例字段,它只能访问静态字段。
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;
}
}
接口的静态字段 因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:
public interface Person {
// 编译器会自动加上public statc final:
int MALE = 1;
int FEMALE = 2;
}
面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法。 现实世界中,我们定义了“人”这种抽象概念,而具体的人则是“小明”、“小红”、“小军”等一个个具体的人。所以,“人”可以定义为一个类(class),而具体的人则是实例(instance)
class是一种对象模版,它定义了如何创建实例,因此,class本身就是一种数据类型:
instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同:
方法可以让外部代码安全地访问实例字段;
方法是一组执行语句,并且可以执行任意逻辑;
方法内部遇到return时返回,void表示不返回任何值(注意和返回null不同);
外部代码通过public方法操作实例,内部代码可以调用private方法;
定义方法:
class [类1] {
[属性1]; // [类1] (){} //默认无参构造函数 [类1] (属性1, 形参1){ this.属性1 = 形参1; // 一般这里的形参命名与属性名一致 } }
class Person { private String name; private int age;
}
class Person { public Person() { } }
class Person { private String name; private int age;
}
class Person { private String name; private int age; }
class Student extends Person { public String hello() { return "Hello, " + name; // 编译错误:无法访问name字段 } }
class Person { protected String name; protected int age; }
class Student extends Person { public String hello() { return "Hello, " + name; // OK! } }
//protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问
class Student extends Person { public String hello() { return "Hello, " + super.name; } }
public class Main { public static void main(String[] args) { Student s = new Student("Xiao Ming", 12, 89);// 编译错误, } }
class Person { protected String name; protected int age;
}
class Student extends Person { protected int score;
}
class Student extends Person { protected int score;
}
class Student extends Person { protected int score;
}
class Book { protected String name; public String getName() {...} public void setName(String name) {...} }
class Student extends Book { protected int score; }
class Student extends Person { protected Book book; protected int score; }