面向对象编程进阶
继承
我们先定义两个类:
package com.advan.inher;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
-----------
package com.advan.inher;
public class Student {
String name;
int age;
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
public void study(){
System.out.println("学习");
}
}
--------
package com.advan.inher;
public class ExtendsTest {
public static void main(String[] args) {
Person p = new Person("小明",18);
p.eat();
Student s = new Student("小王",14,"大数据");
s.eat();
}
}
我们定义了一个Person类和一个Student类,这两个类好像并没有什么关系,但是仔细看看他们的类和方法,发现Person类的属性和方法Student类也都有,那么是不是可以把他们相同的部分提取出来放在一个类中,然后让另一个类继承这个类,这样的话就可以降低代码的冗余。
现在我们改写一下:
package com.advan.inher;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
-----------
package com.advan.inher;
public class Student extends Person{
// extends 继承
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
public void study(){
System.out.println("学习");
}
}
----------
package com.advan.inher;
public class ExtendsTest {
public static void main(String[] args) {
Person p = new Person("小明",18);
p.eat();
Student s = new Student("小王",14,"大数据");
s.eat();
}
}
我们删掉了Student类中继承自Person的属性与方法,但是ExtendsTest依旧没有报错。
我们定义了一个Person类,继承Person的Student类就是Person类的子类,相对于Person类就升级成了Student类的父类。
然后我们还可以定义很多其他的类继承与Person,这样Person类已有的属性与方法就可以不用写了。
好处:
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 继承是多态的前提
语法:
class A extends B{
}
A:子类,派生类,subclass B:父类,超类,基类,supercalss
特殊点:
-
子类内的属性与方法与父类的同名时,以子类为准
package com.advan.inher; public class Person { String name; int age; public Person(){ } public Person(String name,int age){ this.name = name; this.age = age; } public void eat(){ System.out.println("吃饭"); } public void sleep(){ System.out.println("睡觉"); } } ----------- package com.advan.inher; public class Student extends Person{ // extends 继承 String major; public Student(){ } public Student(String name,int age,String major){ this.name = name; this.age = age; this.major = major; } public void eat(){ System.out.println("去食堂吃饭"); } public void study(){ System.out.println("学习"); } } ---------- package com.advan.inher; public class ExtendsTest { public static void main(String[] args) { Person p = new Person(); p.eat(); Student s = new Student(); s.eat(); } } --------- 吃饭 去食堂吃饭
第二次调用输出的是学生类中的eat方法,而不是继承自人类的eat方法。
-
父类中使用private声明的私有属性或方法,子类依旧可以继承,但是受封装性的影响子类无法直接使用。
package com.advan.inher; public class Person { String name; private int age; public Person(){ } public Person(String name,int age){ this.name = name; this.age = age; } public void eat(){ System.out.println("吃饭"); } public void sleep(){ System.out.println("睡觉"); } } ----------- package com.advan.inher; public class Student extends Person{ String major; public Student(){ } public Student(String name,int age,String major){ this.name = name; this.age = age; // 父类的age修改为私有的,这里报错 this.major = major; } public void eat(){ System.out.println("去食堂吃饭"); } public void study(){ System.out.println("学习"); } } ---------- package com.advan.inher; public class ExtendsTest { public static void main(String[] args) { Person p = new Person(); p.eat(); Student s = new Student(); s.eat(); } } --------- 吃饭 去食堂吃饭
注意点:
-
Java只支持单继承或多层继承,不允许多重继承。
意思是子类继承的父类也可以是另一个类的子类,但是子类不能同时有多个父类。
-
父类可以派生多个子类。
-
子父类是相对概念
-
子类直接继承的父类叫做直接父类,间接继承的父类叫做间接父类
-
子类继承直接父类与所有间接父类的属性与方法
-
同名的属性与方法按照最近的一个为准
package com.advan.mlinher;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
-----------
package com.advan.mlinher;
public class Student extends Person{
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
public void eat(){
System.out.println("去食堂吃饭");
}
public void study(){
System.out.println("学习");
}
}
----------
package com.advan.mlinher;
public class Creature extends Student {
public Creature(){
}
public Creature(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
}
----------
package com.advan.mlinher;
public class ExtendsTest {
public static void main(String[] args) {
Creature s = new Creature();
s.eat();
}
}
---------
去食堂吃饭
补充:Object类
我们自己写的类,比如上面的Creature
类,我们在调用他的属性和方法时,处理我们自己定义的,还出现了好多看起来眼熟但是不是我们定义的方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXoaYPla-1590053349245)(https://i.loli.net/2020/05/20/sY9LXgCKnTAjOBF.jpg)]
原因是我们定义的所有类,都隐式继承object类,即object类是所有类的父类。这类似python中的type元类。
是语言内置的一个用来定义类的类。
练习:
题目:
(1) 定义一个ManKind类,包括:
成员变量int sex和int salary
方法void manOrWoman():根据sex的值显示"man"(sex == 1)或"woman"(sex == 0)
方法void employeed():根据salary的值显示"no job"(salary == 0)或"job"(salary != 0)(2) 定义类Kids继承ManKind,并包括:
成员变量int yearsOld
方法printAge()打印yearsOld的值(3) 定义类Kids Test,在类的main方法中实例化Kind的对象someKid,用该对象访问父类的属性与方法
package com.advan.lx;
public class ManKind {
int sex;
int salary;
public ManKind(){
}
public ManKind(int sex,int salary){
this.sex = sex;
this.salary = salary;
}
public void manOrWoman(){
if(sex == 1){
System.out.println("man");
}else if(sex == 0){
System.out.println("woman");
}
}
public void employeed(){
if(salary == 0){
System.out.println("no job");
}else if(salary != 0){
System.out.println("job");
}
}
}
----------
package com.advan.lx;
public class Kids extends ManKind{
int yearsOld;
public Kids(){
}
public Kids(int sex,int salary,int yearsOld){
this.sex = sex;
this.salary = salary;
this.yearsOld = yearsOld;
}
public void printAge(){
System.out.println(yearsOld);
}
}
----------
package com.advan.lx;
public class KidsTest {
public static void main(String[] args) {
Kids someKid = new Kids(1,0,10);
someKid.printAge();
someKid.manOrWoman();
someKid.employeed();
}
}
方法重写
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置,覆盖。在程序执行过程中,子类的方法覆盖父类的方法。
在上面说明继承特殊点的例子其实就是重写的表现:
package com.advan.inher;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
-----------
package com.advan.inher;
public class Student extends Person{
// extends 继承
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
public void eat(){
System.out.println("去食堂吃饭");
}
public void study(){
System.out.println("学习");
}
}
----------
package com.advan.inher;
public class ExtendsTest {
public static void main(String[] args) {
Person p = new Person();
p.eat();
Student s = new Student();
s.eat();
}
}
---------
吃饭
去食堂吃饭
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名参数的方法时,实际执行的是子类重写父类的方法。
重写的规则:
- 方法名与形参列表一致
- 子类重写的方法的权限修饰符不小于父类的权限修饰符(private声明的方法除外)
- 约定俗成:子类中叫重写的方法,父类中叫被重写的方法。
- 被重写方法的返回类型是void,子类重写方法的返回类型也只能是void
- 被重写方法的返回类型是A类型,子类重写方法的返回类型可以是A类或A的子类
- 被重写方法的返回类型是基本数据类型,子类重写方法的返回类型必须是基本数据类型
访问权限修饰符
package com.advan.juri;
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
void methodDefault(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
protected void methodProtected(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
public void methodPublic(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
}
----------
package com.advan.juri;
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
// order.orderPrivate = 1; private 只能在类内部使用
order.orderDefault = 1;
order.orderProtected = 1;
order.orderPublic = 1;
// order.methodPrivate();
order.methodDefault();
order.methodProtected();
order.methodPublic();
}
}
----------
package com.advan.juri1;
import com.advan.juri.Order;
public class Order1 extends Order{
public void method(){
orderProtected = 1; // order1是order的子类,虽然他们不在一个包下面,但是依旧可以使用protected权限修饰符标识的属性和方法
orderPublic = 2;
methodProtected();
methodPublic();
}
}
----------
package com.advan.juri1;
import com.advan.juri.Order;
public class Order2 {
public void methon(){
Order order = new Order();
// 这里不是在一个包下,也没有子父类关系,但是在一个工程中,就可以使用public权限修饰符修饰的属性和方法。
order.orderPublic = 1;
order.methodPublic();
}
}
super
super,可以理解为父类的,可以和this一起理解。
可以用来调用属性、方法、构造器
-
调用属性
package com.advan.supers; public class SuperTest { public static void main(String[] args) { Student s = new Student(); s.con(); } } ---------- package com.advan.supers; public class Person { String name; int age = 30; } ---------- package com.advan.supers; public class Student extends Person{ int age = 18; String major; public void con(){ System.out.println(this.age); System.out.println(super.age); } } ---------- 18 30
-
调用方法
package com.advan.supers; public class SuperTest { public static void main(String[] args) { Student s = new Student(); s.con(); } } ---------- package com.advan.supers; public class Person { String name; int age = 30; public void sleep(){ System.out.println("睡觉"); } } ---------- package com.advan.supers; public class Student extends Person{ int age = 18; String major; public void sleep(){ System.out.println("回宿舍睡觉"); } public void con(){ this.sleep(); super.sleep(); } } ---------- 回宿舍睡觉 睡觉
-
调用构造器
package com.advan.supers; public class Person { String name; int age; public Person(String name,int age){ this.name = name; this.age = age; } } ----------- package com.advan.supers; public class Student extends Person{ String major; int age; public Student(String name,int age,String major){ super(name,age); this.major = major; } public void show(){ System.out.println(this.age); System.out.println(super.age); } } ---------- package com.advan.supers; public class SuperTest { public static void main(String[] args) { Student s = new Student("小明",20,"大数据"); s.show(); } } --------- 0 20
子类对象实例化
子类继承父类后,就获得了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
当我们通过子类构造器创建子类对象时,我们会直接或间接调用父类构造器,然后一直调用到object类构造器为止。然后子类对象才算正式初始化完成。
多态
多态就是一句话:一个对象指向两个类。
多态即多种状态,一个状态表示一个类。
我记得我在python类那一个博文里举的例子是:
小明是个学生,也是个北京人,他具有两种表示身份的状态,这就是多态。
java和python的多态不同的是:
python是子类对象是子类类型的同时也是父类类型。
java是使用子类构造器实例化父类对象。
前者可以有多重状态(python支持多继承),后者就只有两种状态(子类,父类)。
语法:
父类名 对象名 = new 子类构造器;
package com.advan.inher;
public class Person {
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
-----------
package com.advan.inher;
public class Student extends Person{
String major;
public Student(){
}
public Student(String name,int age,String major){
this.name = name;
this.age = age;
this.major = major;
}
public void eat(){
System.out.println("去食堂吃饭");
}
public void study(){
System.out.println("学习");
}
}
----------
package com.advan.inher;
public class ExtendsTest {
public static void main(String[] args) {
Person p = new Student();
p.eat();
System.out.println(p instanceof Person);
System.out.println(p instanceof Student);
// instanceof关键字 判断对象是什么类型,返回布尔值
}
}
---------
去食堂吃饭
true
true
多态本质是一个转型的过程,多态是向上转型(使用子类构造器声明父类对象,有用小子造老子的赶脚)。
还有一个向下转型(使用父类构造器声明子类对象,这才是真二八经的老子造小子)
语法:
子类名 变量名 =(子类名) 父类对象;
package com.advan.inher;
public class ExtendsTest {
public static void main(String[] args) {
Person p = new Person();
Student s = (Student) p;
// int i = 1;
// double d = (int) i;
}
}
没错,就是强制类型转换。