面向对象基础
学习资料来自继承 - Java教程 - 廖雪峰的官方网站
目录
方法重载
如果有一系列方法,功能类似,只是参数有所不同,就可以把这字方法名成同名方法:
class Hello {
public void hello() {
System.out.println("Hello");
}
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 + "!");
}
}
}
这种方法名相同,但各自参数不同,称为方法重载(Overload)。
通常,方法重载的返回值类型都是相同的。
举个例子:
String类提供了多个重载方法indexOf(),可以查找子串:
- int indexOf(int ch):根据字符的Unicode码查找
- int indexOf(String str):根据字符查找
- int indexOf(int ch,int fromIndex):指定起始位置,根据字符查找
- int indexOf(String str,int fromIndex):指定起始位置,根据字符串查找
public class Main {
public static void main(String[] args) {
String s = "Test string";
int n1 = s.indexOf('t');
int n2 = s.indexOf("st");
int n3 = s.indexOf("st",4);
System.out.println(n1);
System.out.println(n2);
System.out.println(n3);
}
}
练习:
public class Main {
public static void main(String[] args) {
Person ming = new Person();
Person hong = new Person();
ming.setName("Xiao Ming");
hong.setName("Xiao ","Hong");
System.out.println(ming.getName());
System.out.println(hong.getFirstName() + hong.getSunName());
}
}
class Person {
private String name;
private String sunName;
private String firstName;
public String getName() {
return name;
}
public String getSunName() {
return sunName;
}
public String getFirstName() {
return firstName;
}
public void setName(String name) {
this.name = name;
}
public void setName(String fitstName,String sunName) {
this.firstName = fitstName;
this.sunName = sunName;
}
}
继承
若要定义一个类,这个类包含了已有类的字段和方法,只是增加了一些字段和方法,可以用继承从而在这个类中不写重复代码。
继承是面向对象编程中非常强大的一种机制,可以复用代码,只需要为新的类编写新增功能即可。
Java用extends关键字来实现继承:
class Person {
private String name;
private int 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;
}
}
class Student extends Person {
private int score;
public void getScore(int score) {
this.score = score;
}
}
子类自动获得了父类所有字段,严禁定义与父类重名的字段。
在OOP的术语中,把Person成为超类,基类,父类;把Student成为子类,扩展类
继承树
在Java中 ,没有明确写extends的类,编译器自动加上extends Object。所以,除了Object,任何类都会继承自某个类,Java只允许一个class继承自一个类。
protected
继承的特点:子类无法访问父类的private字段或者private方法。所以下面例子中Student无法访问Person的name字段和age字段:
class Person {
private String name;
private int age;
}
class Student extends Person {
public String hello() {
return "Hello," + name;//会编译错误
}
}
为了让子类可以访问父类的字段,要把private改为protected。用protected修饰的字段可以被子类访问。
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello," +name;//可以正常编译
}
}
以上,protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类、子类的子类访问。
super
表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。例如:
class Student extends Person {
public String hello() {
return "Hello," + super.name;
}
}
这里使用super.name或者this.name效果一致,编译器会自动定位到父类的name字段。
但是,在某些情况,必须使用super,如:
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;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name,int age,int score) {
this.score = score;
}
}
编译错误为:在Student的构造方法中,无法调用Person的构造方法。
在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法,编译器会自动加一个super·();所以,Student类的构造方法实际是这样:
class Student extends Person {
protected int score;
public Student(String namekint age,int score; {
super();
this.score = score;
}
}
由于Person类并没有无参数的构造方法,所以编译失败,可以调用Person存在的某个构造方法,如:
class Student extends Person {
protected int score;
public Student(String name,int age,int score) {
super(name,age);
this.score = score;
}
}
结论:如果父类没有默认构造方法,子类就必须显示调用super()并给出参数以便编译器定位到父类的一个合适的构造方法。子类不会继承父类的任何构造方法,子类默认的构造方法是编译器自动生成的,而不是继承的。
阻止继承
正常情况下,只要某个类没有final修饰符,那么任何类都可以从该class继承。用sealed修饰class,并通过permits明确写出能够从该class类继承的子类名称。
比如定义一个Shape类:
public sealed class Shape permits Rect,Circle,Triangle {
...
}
这就是一个sealed类,只允许指定的3个类继承它,
public final class Rect extends Shape{...}
// 没问题,因为Rect出现在Shape的permits列表中
// 但是,如果定义一个Ellipse就会报错
// 因为Ellipse并未出现在Shape的permits列表中。
public final class Sllipse extends Shape {...}
// Compile error: class is not allowed to extend sealed class: Shape
这种sealed类主要用于一些框架,防止继承被滥用。
sealed在Java15中目前是预览状态,要启用它必须使用参数--enable-preview和--source15
向上转型
如果Student是Person继承下来的,那么,一个引用类型为Person的变量,可以指向Student。Student拥有父类Person所有功能,Person类型的变量可以指向Student类型的实例。
Person p = new Student();
这种把一个子类类型安全的变为父类类型的赋值,被称为向上转型(upcasting)
Student s = new Student();
Person p = s;// upcasting,ok
Object o1 = p;// upcasting,ok
Object o2 = s;// upcasting,ok
继承树为:Student > Person > Object,所以,可以把Student类型转为Person或Object
向下转型
如果一个父类类型强制转为子类类型,就是向下转型(downcasting)
Person p1 = new Student();// upcasting,ok
Person p2 = new Person();
Student s1 = (student) p1;// ok
Student s2 = (Student) p2;// runtime error! ClassCastException!
为了避免向下转型出现错误Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型。
Person p = new Person();
System.out.println(p instancaof Person);// true
System,out.ptintln(p instanceof Student);// false
Student s = new Student();
System.out.println(s instanceof Person);// true
System.out.println(s instanceof Student);// true
Student n = null;
System.out.println(n instanceof Student);ture
instanceof实际上判断一个变量所指的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false
利用instanceof,在向下转型前可以先判断:
Person p = new Student();
if (p instanceof Student) {
// 只有判断成功才会向下转型
Student s = (Student) p;
}
从Java14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。
Object obj = "hello";
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
// 可以改写为:
public class Main {
public static void main(String[] args) {
Object obj = "hello";
if(obj instanceof String s) {
// 直接使用变量s
System.out.println(s.yoUpperCase());
}
}
}
区分继承和组合
is关系用继承,has关系用组合:
class Student extends Person {
protected Book book;
protected int score;
}
练习
public class Main {
public static void main(String[] args) {
Person p = new Person("小明",12);
Student s = new Student("小红",29,99);
Student ps = new PrimaryStudent("小军",9,100,5);
System.out.println(ps.getScore());
}
}
class Person {
protected String name;
protected int age;
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;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public int getScore() {
return score;
}
}
class PrimaryStudent extends Student {
protected int grade;
public PrimaryStudent(String name, int age, int score, int grade) {
super(name, age, score);
}
}
小结:
继承是面向对象编程的一种强大的代码复用方式。
只允许单继承,所有类最终的根类是Object
protected允许子类访问父类的字段和方法
子类的构造方法可以通过super()调用父类的构造方法