【Java】面向对象(中级) - 1

面向对象(中级)

1. 包

1.1 包的基本介绍

包的三大作用

  1. 区分相同名字的类

  2. 当类很多时,可以很好的管理类

  3. 控制访问范围

包基本语法

package com.ygh;

说明:

  1. package 关键字,表示打包
  2. com.ygh; 表示包名

包的本质分析

包的本质实际上就是创建不同的文件夹/目录来保存类文件

在这里插入图片描述

2. 封装

2.1 封装介绍

封装( encaps ulation)就是把抽象出的数据 [属性] 和对数据的操作 [方法] 封装在一起,

数据被保护在内部,程序的其它部分只有通过被授权的操作 [方法],才能对数据进行操作。

封装的理解和好处

  • 隐藏实现细节:方法(连接数据库) <== 调用(传入参数…)

  • 可以对数据进行验证,保证安全合理

2.2 封装的实现步骤

  1. 将属性进行私有化 private【不能直接修改属性】

  2. 提供一个公共的 (public)set 方法,用于对属性判断并赋值

public void setXxx(类型参数名){ 	//Xxx表示某个属性
	// 加入数据验证的业务逻辑
	属性 = 参数名;
}
  1. 提供一个公共的 get 方法,用于获取属性的值
public XX getXxx() {	// 权限判断
	return xx;
}

编写程序,不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。

年龄合理就设置,否则给默认年龄必须在1-120,年龄,工资不能直接查看,name的长度在 2 - 6 字符

public class Encapsulation01 {

	public static void main(String[] args) {
		Person person = new Person();

		person.setName("jack");
		person.setAge(18);
		person.setSalary(5000);
		System.out.println(person.info());	
	}
}

/**
 *
 * 编写程序,不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。
 * 年龄合理就设置,否则给默认年龄必须在1-120,
 * 年龄,工资不能直接查看,
 * name的长度在 2 - 6 字符
 *
 * */
class Person {
	public String name;	//名字 公开
	private int age;	//年龄 私有化
	private double salary;	//工资 私有化

	//setXxx 和 getXxx
	//根据需求完善代码
	public String getName() {
		return name;
	}

	public void setName(String name) {
		//加入对数据的校验
		if (name.length() >= 2 && name.length() <= 6) {
			this.name = name;
		} else {
			System.out.println("名字长度不对,需要在 2 - 6 个字符之间,默认名字");
			this.name = "无名";
		}
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		//判断
		if (age >= 1 && age <= 120) {
			this.age = age;
		} else {
			System.out.println("你设置的年龄不对,年龄需要在 1 - 120 之间,给默认年龄 18");
			this.age = 18;	//给一个默认年龄
		}
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	//写一个方法,返回属性信息
	public String info() {
		return "信息为 name= " + name + " age = " + age + " 薪水 = " + salary;
	}
}

2.3 封装与构造器

public class Encapsulation02 {

	public static void main(String[] args) {
		//如果使用构造器指定属性,那么定义的私有属性就是去效果
		Person02 smith = new Person02("smith", 18, 5000);
		System.out.println("=====smith的信息=====");
		System.out.println(smith.info());

		System.out.println("=====00000=====");
		//将set方法写在构造器后,set方法会对私有属性产生效果
		Person02 tom = new Person02("很长很长很长很长的名字", 500, 5000);
		System.out.println("=====tom的信息=====");
		System.out.println(tom.info());
	}
}

/**
 *
 * 编写程序,不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。
 * 年龄合理就设置,否则给默认年龄必须在1-120,
 * 年龄,工资不能直接查看,
 * name的长度在 2 - 6 字符
 *
 * */
class Person02 {
	public String name;	//名字 公开
	private int age;	//年龄 私有化
	private double salary;	//工资 私有化

	//有三个属性的构造器
	public Person02() {
		
	}

	public Person02(String name, int age, double salary) {
		//
		//this.name = name;
		//this.age = age;
		//this.salary = salary;
		//
		//可以将set方法写在构造器中,这样就可以保护私有属性
		setName(name);
		setAge(age);
		setSalary(salary);
	}

	//setXxx 和 getXxx
	//根据需求完善代码
	public String getName() {
		return name;
	}

	public void setName(String name) {
		//加入对数据的校验
		if (name.length() >= 2 && name.length() <= 6) {
			this.name = name;
		} else {
			System.out.println("名字长度不对,需要在 2 - 6 个字符之间,默认名字");
			this.name = "无名";
		}
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		//判断
		if (age >= 1 && age <= 120) {
			this.age = age;
		} else {
			System.out.println("你设置的年龄不对,年龄需要在 1 - 120 之间,给默认年龄 18");
			this.age = 18;	//给一个默认年龄
		}
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	//写一个方法,返回属性信息
	public String info() {
		return "信息为 name= " + name + " age = " + age + " 薪水 = " + salary;
	}
}

2.4 封装练习

创建程序在其中定义两个类:Account 和 AccountTest 类体会 Java 的封装性。

  1. Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)、密码(必须是六位),如果不满足,则给出提示信息,并给默认值(程序员自己定)
  2. 通过 setXxx 的方法给 Account 的属性赋值。
  3. 在 AccountTest 中测试
public class Account {
	//为了封装,将3个属性设置为private
	private String name;
	private double balance;
	private String pwd;

	//提供两个构造器
	public Account() {

	}

	public Account(String name, double balance, String pwd) {
		//this.name = name;
		//this.balance = balance;
		//this.pwd = pwd;
		//
		//为了不影响属性的私有性
		this.setName(name);
		this.setBalance(balance);
		this.setPwd(pwd);
	}

	//姓名(长度为2位3位或4位)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		if (name.length() >= 2 && name.length() <= 4) {
			this.name = name;
		} else {
			System.out.println("姓名要求(长度为2位3位或4位),默认值 无名");
			this.name = "无名";
		}
	}

	public double getBalance() {
		return balance;
	}

	//余额(必须 > 20)
	public void setBalance(double balance) {
		if (balance > 20) {
			this.balance = balance;
		} else {
			System.out.println("余额(必须>20),默认值为0");
			this.balance = 0;
		}
	}

	public String getPwd() {
		return pwd;
	}
	
	//密码(必须是六位)
	public void setPwd(String pwd) {
		if (pwd.length() == 6) {
			this.pwd = pwd;
		} else {
			System.out.println("密码(必须是六位),默认密码为 000000");
			this.pwd = "000000";
		}
	}

	//显示账号信息
	public void showInfo() {
		System.out.println("账号信息 name = " + name + " 余额=" + balance + " 密码=" + pwd);
	}
}
public class AccountTest {
	public static void main(String[] args) {
		//创建 Account
		Account account1 = new Account();
		account1.setName("jack");
		account1.setBalance(600);
		account1.setPwd("123456");
		
		account1.showInfo();

		//创建 Account
		Account account2 = new Account();
		account2.setName("jack123456");
		account2.setBalance(6);
		account2.setPwd("123");
		
		account2.showInfo();
		
	}
}

3. 继承

3.1 为什么需要继承?

编写了两个类,一个是Pupil类(小学生),一个是Graduate(大学生)。

  • 问题:两个类的属性和方法有很多是相同的,怎么办?
    • 继承(代码复用性)
//小学生 -> 模拟小学生考试的情况
public class Pupil {
	public String name;
	public int age;
	private double score;	//成绩

	public void setScore(double score) {
		this.score = score;
	}

	public void testing() {
		System.out.println("小学生 " + name + " 正在考小学数学...");
	}

	public void showInfo() {
		System.out.println("小学生名 " + name + " 年龄 " + age + " 成绩 " + score);
	}
}
//大学生类 -> 模拟大学生考试的简单情况
public class Graduate {
	public String name;
	public int age;
	private double score;	//成绩
	public void setScore(double score) {
		this.score = score;
	}

	public void testing() {	//和pupil不一样
		System.out.println("大学生 " + name + " 正在考大学数学...");
	}

	
	public void showInfo() {	//和pupil不一样
		System.out.println("大学生名 " + name + " 年龄 " + age + " 成绩 " + score);
	}
}

public class Extends {
    public static void main(String[] args) {
            Pupil pupil = new Pupil();
            pupil.name = "小明";
            pupil.age = 10;
            pupil.testing();
            pupil.setScore(60);
            pupil.showInfo();

            Graduate graduate = new Graduate();
            graduate.name = "大明";
            graduate.age = 22;
            graduate.setScore(100);
            graduate.showInfo();
    }
}

3.2 继承基本介绍

继承可以解决代码复用,让我们的编程更加靠近人类思维。

当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,

在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,

只需要通过 extends 来声明继承父类即可。

画出继承的示意图

在这里插入图片描述

继承的基本语法

class 子类 extends 父类 {

}
  1. 子类就会自动拥有父类定义的属性和方法
  2. 父类又叫超类,基类。
  3. 子类又叫派生类。
//小学生 -> 模拟小学生考试的情况
public class Pupil extends Student {
	public void testing() {
		System.out.println("小学生 " + name + " 正在考小学数学...");
	}
}
//小学生 -> 模拟小学生考试的情况
public class Pupil extends Student {
	public void testing() {
		System.out.println("小学生 " + name + " 正在考小学数学...");
	}
}
//大学生类 -> 模拟大学生考试的简单情况
public class Graduate extends Student {
	public void testing() {	//和pupil不一样
		System.out.println("大学生 " + name + " 正在考大学数学...");
	}
}
public class Extends {
	public static void main(String[] args) {
		com.ygh.extends02.Pupil pupil = new Pupil();
		pupil.name = "小明";
		pupil.age = 10;	
		pupil.testing();
		pupil.setScore(60);
		pupil.showInfo();

		System.out.println("===== =====");
		com.ygh.extends02.Graduate graduate = new Graduate();
		graduate.name = "大明";
		graduate.age = 22;
		graduate.testing();
		graduate.setScore(100);
		graduate.showInfo();
	}
}

继承给编程带来的便利

  • 代码的复用性提高了
  • 代码的扩展性和维护性提高了

3.3 继承深入讨论/细节问题

  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性不能在子类直接访问,要通过公共的方法去访问
public class Base {	//父类
	//4个属性
	public int n1 = 100;
	protected int n2 = 200;
	int n3 = 300;
	private int n4 = 400;
	
	public Base() {	//无参构造器
		System.out.println("base()...");
	}
	
	//父类提供一个 public 的方法,返回 n4
	public int getN4() {
		return n4;
	}
	
	public void test100() {
		System.out.println("test100()...");
	}
	
	protected void test200() {
		System.out.println("test200()...");
	}
	
	void test300() {
		System.out.println("test300()...");
	}
	
	private void test400() {
		System.out.println("test400()...");
	}
	
	//父类提供一个 public 的方法
	public void callTest400() {
		test400();
	}
}
public class Sub extends Base {	//子类
	public Sub() {	//构造器
		System.out.println("Sub()...");
	}
	
	public void sayOk() {
		//非私有的属性和方法可以在子类直接访问
		//但是私有属性不能在子类直接访问
		System.out.println(n1 + " " + n2 + " " + n3 /*+ " " + n4*/);	//使用 n4 会报错
		test100();
		test200();
		test300();
		//test400();	//使用 test400 报错
		
		//要通过公共的方法去访问
		System.out.println("n4 = " + getN4());
		callTest400();	//通过callTest400 调用 test400 方法
	}
}
public class ExtendsDetail {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			Sub sub = new Sub();
			sub.sayOk();
	}

}
  • 子类必须调用父类的构造器,完成父类的初始化

在这里插入图片描述

  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过

1

public class Base {	//父类
	//4个属性
	public int n1 = 100;
	protected int n2 = 200;
	int n3 = 300;
	private int n4 = 400;
	
	public Base() {	//无参构造器
		System.out.println("父类 base() 被调用...");
	}
	
	public Base(String name, int age) {	//有参构造器
		System.out.println("父类 base(String name, int age) 被调用...");
	}
	
}
public class Sub extends Base {	//子类
	public Sub() {	//构造器
		//super();	//默认调用父类的无参构造器,写不写都一样执行的
		System.out.println("子类 Sub() 被调用...");
	}
	
	//当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
	public Sub(String name) {
		System.out.println("子类 Sub(String name ) 构造器被调用...");
	}

}
public class ExtendsDetail {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("=====第一个对象=====");
		Sub sub = new Sub();	//创建子类对象 sub
		//=====第一个对象=====
		//父类 base() 被调用...
		//子类 Sub() 被调用...
		
		System.out.println("=====第二个对象=====");
		Sub sub2 = new Sub("jack");	//创建子类对象 sub2
		//=====第二个对象=====
		//父类 base() 被调用...
		//子类 Sub(String name ) 构造器被调用...
	}

}

2

public class Base {	//父类
	//4个属性
	public int n1 = 100;
	protected int n2 = 200;
	int n3 = 300;
	private int n4 = 400;
	
	//注释 Base 的无参构造器
//	public Base() {	//无参构造器
//		System.out.println("父类 base() 被调用...");
//	}
	
	public Base(String name, int age) {	//有参构造器
		System.out.println("父类 base(String name, int age) 被调用...");
	}
	
}
public class Sub extends Base {	//子类
	//当父类 Base 注释无参构造器后,Sub子类的构造器会出现报错
	//因为没有调用的默认无参构造器
	//所以用 super 加上 Base 定义的有参构造器
	public Sub() {	//构造器
		super("smith", 10);
		System.out.println("子类 Sub() 被调用...");
	}
	
	//当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
	public Sub(String name) {
		super("tom", 30);
		System.out.println("子类 Sub(String name ) 构造器被调用...");
	}

}
public class ExtendsDetail {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                System.out.println("=====第一个对象=====");
                Sub sub = new Sub();    //创建子类对象 sub
                //=====第一个对象=====
                //父类 base(String name, int age) 被调用...
                //子类 Sub() 被调用...

                System.out.println("=====第二个对象=====");
                Sub sub2 = new Sub("jack");     //创建子类对象 sub2
                //=====第二个对象=====
                //父类 base(String name, int age) 被调用...
                //子类 Sub(String name ) 构造器被调用...
        }

}
  • 如果希望指定去调用父类的某个构造器,则显式的调用一下 super (参数列表)
  • super 在使用时,必须放在构造器第一行( super 只能在构造器中使用)
  • super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
public class Base {	//父类
	//4个属性
	public int n1 = 100;
	protected int n2 = 200;
	int n3 = 300;
	private int n4 = 400;
	
	public Base() {	//无参构造器
		System.out.println("父类 base() 被调用...");
	}
	
	public Base(String name, int age) {	//有参构造器
		System.out.println("父类 base(String name, int age) 被调用...");
	}
	
	public Base(String name) {	//有参构造器
		System.out.println("父类 base(String name) 被调用...");
	}
	
}
public class Sub extends Base {	//子类
	
	/* 细节:如果希望指定去调用父类的某个构造器,则显式的调用一下super(参数列表) */
	public Sub(String name, int age) {
		//1. 调用父类的无参构造器,或者什么也不写表示调用默认 super(); 
//		super();	//父类的无参构造器
//		System.out.println("子类 Sub() 构造器被调用...");
		
		//2. 调用父类的 Base(Strinig name)构造器
//		super("ygh");
//		System.out.println("子类 Sub(String name) 构造器被调用...");
		
		//3. 调用父类的 Base(STring name, int age) 构造器
		/* 细节:super在使用时,必须放在构造器第一行(super 只能在构造器中使用) */
		super("ich", 23);
		/* 细节:super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器 */
		//this("ygh");	//报错
		System.out.println("子类 Sub(String name, int age) 构造器被调用...");
	}
	
	public Sub() {	//构造器
		super("smith", 10);
		System.out.println("子类 Sub() 被调用...");
	}
	
	public Sub(String name) {
		super("tom", 30);
		System.out.println("子类 Sub(String name) 构造器被调用...");
	}

}
public class ExtendsDetail {

	public static void main(String[] args) {
		System.out.println("=====第三个对象=====");
		Sub sub3 = new Sub("king", 10);	//创建子类对象 sub3
	}

}
  • java 所有类都是 Object 类的子类,Object 是所有类的基类。
  • 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)

在这里插入图片描述

  • 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制
    • 思考:如何让A类继承B类和C类?
    • 【 A 继承 B ,B 继承 C 】

在这里插入图片描述

  • 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
    • Person is a Music?
    • Person Music
    • Music extends Person //不合理
    • Animal
    • Cat extents Animal //合理

3.4 继承本质

  • 子类继承父类,创建子类对象是,内存发生了什么?
    • 当子类对象创建好后,建立查找的关系
class GrandPa {	//爷爷类
	String name = "大头爷爷";
	String hobby = "旅游";
}
class Father extends GrandPa {	//父类
	String name = "大头爸爸";
	int age = 39;
}
class Son extends Father {	//子类
	String name = "大头儿子";
}
/* 讲解继承的本质 */
public class ExtendsTheory01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			Son son = new Son();	//内存的布局
	}

}

在这里插入图片描述

class GrandPa {	//爷爷类
	String name = "大头爷爷";
	String hobby = "旅游";
	double money = 3000;
}
class Father extends GrandPa {	//父类
	String name = "大头爸爸";
	//int age = 39;
	//改成私有属性
	private int age = 39;	//设置为私有属性
	
	private double money = 9000;
	
	//设置方法
	public int getAge() {
		return age;
	}
}
class Son extends Father {	//子类
	String name = "大头儿子";
}
/* 讲解继承的本质 */
public class ExtendsTheory02 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			Son son = new Son();	//内存的布局
			
			//注意,按照查找关系来返回信息
			//首先看子类是否有该属性
			//如果子类有这个属性,并且可以访问,则返回信息
			//如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
			//如果父类没有就按照(3)的规则,继续找上级父类,直到Object...
			System.out.println(son.name);	//返回 大头儿子
			
			//System.out.println(son.age);	//返回 39
			//无法访问私有属性
			System.out.println(son.getAge());	//返回 39
			
			System.out.println(son.hobby);	//返回 旅游
			
			//即使爷爷类有 money 属性,也会报错
			//因为无法跳过 父类的私有属性 money
			//System.out.println(son.money);
	}

}

3.5 练习

1:main中:B b = new B(); 会输出什么?

public class ExtendsExercise01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		B b = new B();
		//输出:
		//a
		//b name
		//b
	}

}

class A {
	A() {
		System.out.println("a");
	}
	A(String name) {
		System.out.println("a name");
	}
}

class B extends A {
	B() {
		this("abc");
		System.out.println("b");
	}
	B(String name) {
		//隐藏了一个super();
		System.out.println("b name");
	}
}

2:main中:E e = new E(); 会输出什么?

public class ExtendsExercise02 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		E e = new E();
		//输出:
		//C类的无参构造
		//D类的无参构造
		//hahahaD类的有参构造
		//E类的有参构造
	}

}

class C {	//C类
	public C() {
		System.out.println("C类的无参构造");
	}
}

class D extends C {	//D类继承C类
	public D() {
		System.out.println("D类的无参构造");
	}
	public D(String name) {
		System.out.println(name + "D类的有参构造");
	}
}

class E extends D {	//E类继承D类
	public E() {
		this("hello");
		System.out.println("E类的无参构造");
	}
	
	public E(String name) {
		super("hahaha");
		System.out.println("E类的有参构造");
	}
}

在这里插入图片描述

3 编写程序

编写 Computer 类,包含CPU、内存、硬盘等属性,getDetails方法用于返回 Computer 的详细信息

编写 PC 子类,继承 Computer 类,添加特有属性【品牌brand】

编写 NotePad 子类,继承 Computer 类,添加特有属性【color】

编写 Test 类,在 main 方法中创建 PC 和 NotePad 对象,分别给对象中特有的属性赋值,以及从 Computer 类继承的属性赋值,并使用方法并打印输出信息。

/* 
 * 编写 Computer 类,包含CPU、内存、硬盘等属性,
 * getDetails方法用于返回 Computer 的详细信息
 * */
public class Computer {
	private String cpu;
	private int memory;
	private int disk;
	
	public Computer(String cpu, int memory, int disk) {
		this.cpu = cpu;
		this.memory = memory;
		this.disk = disk;
	}
	
	//返回 Computer 信息
	public String getDetails() {
		return "cpu = " + cpu + " memory = " + memory + " disk = " + disk;
	}
	
	public String getCpu() {
		return cpu;
	}
	
	public void setCpu(String cpu) {
		this.cpu = cpu;
	}
	
	public int getMemory() {
		return memory;
	}
	
	public void setMemory(int memory) {
		this.memory = memory;
	}
	
	public int getDisk() {
		return disk;
	}
	
	public void setDisk(int disk) {
		this.disk = disk;
	}
	
	
}
//public class PC extends Computer {
//	//刚建立就报错,是因为父类没有无参构造器了,子类 PC 没有找到就报错
//}

/* 编写 PC 子类,继承 Computer 类,添加特有属性【品牌brand】 */
public class PC extends Computer {
	private String brand;
	
	//根据继承规则,调用父类的有参构造器
	//继承设计的基本思想,父类的构造器完成父类属性的初始化
	//子类的构造器完成子类属性的初始化
	public PC(String cpu, int memory, int disk, String brand) {
		super(cpu, memory, disk);	//super 调用有参构造器,避免报错
		this.brand = brand;
	}
	
	public void setBrand(String brand) {
		this.brand = brand;
	}
	
	public void printInfo() {
		System.out.println("PC信息:");
		//System.out.println(getCpu() + getMemory() + getDisk());
		//调用父类的 getDetails 方法,得到相关属性
		System.out.println(getDetails() + " brand = " + brand);
	}
}
/* 编写 NotePad 子类,继承 Computer 类,添加特有属性【color】 */
public class NotePad extends Computer {
	private String color;
	public NotePad(String cpu, int memory, int disk, String color) {
		super(cpu, memory, disk);
		this.color = color;
	}

	public void setColor(String color) {
		this.color = color;
	}
	
	public String getColor(String color) {
		return color;
	}
	
	public void printInfo() {
		System.out.println("NotePad信息:");
		System.out.println(getDetails() + " color = " + color);
	}
}
/*
 * 编写 Test 类,在 main 方法中创建 PC 和 NotePad 对象,
 * 分别给对象中特有的属性赋值,以及从 Computer 类继承的属性赋值,
 * 并使用方法并打印输出信息。
 * */
public class ExtendsExercise03 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		PC pc = new PC("intel", 16, 1024, "IBM");
		pc.printInfo();
		
		NotePad note = new NotePad("xiaomi", 8, 500, "break");
		note.printInfo();
	}

}

4. super 关键字

4.1 基本介绍

super 代表父类的引用,用于访问父类的属性、方法、构造器

基本语法

  • 访问父类的属性,但不能访问父类的 private 属性
    • super.属性名;
  • 访问父类的方法,不能访问父类的 private 方法
    • super.方法名(参数列表);
  • 访问父类的构造器(这点前面用过):
    • super(参数列表);
    • 只能放在构造器的第一句,只能出现一句!
public class A {
	//4个属性
	public int n1 = 100;
	protected int n2 = 200;
	int n3 = 300;
	private int n4 = 400;
	
	public A() {
		
	}
	
	public A(String name) {
		
	}
	
	public A(String name, int age) {
		
	}
	
	public void test100() {
		
	}
	
	protected void test200() {
		
	}
	
	void test300() {
		
	}
	
	private void test400() {
		
	}
}
public class B extends A {
	//访问父类的属性,但不能访问父类的private属性
	//super.属性名;
	public void hi() {
		//System.out.println(super.n1 + " " + super.n2 + " " + super.n3 + " " + super.n4);
		//super.n4 提示报错
		System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
	}
	
	//访问父类的方法,不能访问父类的 private 方法
	//super.方法名(参数列表);
	public void ok() {
		super.test100();
		super.test200();
		super.test300();
		//super.test400();	//不能访问父类 private 方法
	}
	
	//访问父类的构造器
	//super(参数列表);
	//只能放在构造器的第一句,只能出现一句!
	
	//不是构造器
//	public void hello() {
//		super();
//	}
	
	public B() {
		//super();
		//super("jack");
		super("tom", 10);
	}
}

4.2 使用细节

  • 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
  • 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!
public class C {
	public int n1 = 100;
	
	public void cal() {
		System.out.println("C 类的 cal() 方法...");
	}
}
public class D extends C {
	public int n1 = 888;
	
	public void cal() {
		System.out.println("D 类的 cal() 方法...");
	}
	
	public void sum() {
		System.out.println("D 类的 sum() 方法...");
		//希望调用父类 A 的 cal 方法
		
		//第一种方法
		//找cal方法时,顺序是,
		//先找本类,如果有,则调用,
		//如果没有,则找父类(如果有,并可以调用,则调用)
		//如果父类没有,则继续找父类的父类,整个规则,就是一样的,直接 Object 类
		//提示:	如果查找方法的过程中,找到了,但是不能访问,则报错
		//		如果查找方法的过程中,没有找到,则提示方法不存在
		//cal();	//输出 :C 类的 cal() 方法...
		
		//当本类定义相同的方法名时,cal(); 调用的是本类的cal() 方法
		cal();	//D 类的 cal() 方法...
		
		//第二种方法
		//this.cal();//等价 cal();	
		//输出 :D 类的 cal() 方法...
		
		//当本类定义相同的方法名时,this.cal(); 调用的是本类的cal() 方法
		this.cal();	//输出 :C 类的 cal() 方法...
		
		//第三种方法
		//找cal() 方法 (super.cal();)的顺序是直接查找父类属性,其他的规则一样
		super.cal();
		
		// n1 和 this.n1 查找的规则是
		//(1)先找本类,如果有,则调用
		//(2)如果没有,则找父类(如果有,并可以调用,则调用)
		//(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直接 Object 类
		//提示:	如果查找方法的过程中,找到了,但是不能访问,则报错
		//		如果查找方法的过程中,没有找到,则提示方法不存在
		System.out.println(n1);	//888
		System.out.println(this.n1);	//888
		
		//找n1 (super.n1)的顺序是直接查找父类属性,其他的规则一样
		System.out.println(super.n1);	//100

	}
}
public class super01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		D d = new D();
		d.sum();
	}

}
  • super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

super 和 this 的比较

No.区别点thissuper
1访问属性访问本类中的属性,如果本没有此属性则从父类中继续查找从父类开始查找属性
2调用方法访问本类中的方法,如果本此属性则从父类中继续查找直接访问父类中的方法
3调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在子构造器的首
4特殊表示当前对象子类中访问父类对象

5. 方法重写/覆盖

5.1 基本介绍

简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法

public class Animal {
	public void cry() {
		System.out.println("动物叫唤...");
	}
}
public class Dog extends Animal {
	//1. 因为Dog是 Animal子类 
	//2.Dog的 cry方法和 Animal的 cry 定义形式一样(名称、返回类型、参数)
	//3.这时我们就说 Dog的cry方法,重写了Animal的cry方法
	public void cry() {
		System.out.println("小狗汪汪叫...");
	}
}
public class Override01 {
	public static void main(String[] args) {
		//演示方法重写的情况
		Dog dog = new Dog();
		dog.cry();
	}
}

5.2 注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件

  • 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样

  • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类

    • 比如父类返回类型是 Object,子类方法返回类型是String
  • 子类方法不能缩小父类方法的访问权限【public > protected > 默认 > private 】

public class Animal {
	public void cry() {
		System.out.println("动物叫唤...");
	}
	
	public Object m1() {
		return null;
	}
	
	public String m2() {
		return null;
	}
	
	public AAA m3() {
		return null;
	}

	public void eat() {
		
	}
}
public class Dog extends Animal {
	//1. 因为Dog是 Animal子类 
	//2.Dog的 cry方法和 Animal的 cry 定义形式一样(名称、返回类型、参数)
	//3.这时我们就说 Dog的cry方法,重写了Animal的cry方法
	public void cry() {
		System.out.println("小狗汪汪叫...");
	}
	
	//细节:子类方法的返回类型和父类方法返回类型一样,
	//	或者是父类返回类型的子类,返回类型是 Object,
	//	子类方法返回类型是String
	//public Object m1() {
	public String m1() {	//也没报错,因为Object的子类是String
		return null;
	}
	
	//这里Object不是String的子类,因此编译错误
	//public Object m2() {	//报错,不能
	public String m2() {
		return null;
	}

	//子类方法的返回类型和父类方法返回类型一样,
	//或者是父类返回类型的子类
	//public AAA m3() {	//没报错
	public BBB m3() {
		return null;
	}

	//protected void eat() {	//子类方法不能缩小父类方法的访问权限
    //public > protected > 默认 > private
	public void eat() {	

	}
}

class AAA {
	
}

class BBB extends AAA {
	
}
public class Override01 {
	public static void main(String[] args) {
		//演示方法重写的情况
		Dog dog = new Dog();
		dog.cry();
	}
}

5.3 方法的重写和重载比较

名称发生范围方法名形参列表返回类型修饰符
重载(overload)本类必须一样类型,个数或者顺序至少有一个不同无要求无要求
重写(override)父子类必须一样相同重写的方法,返回的类型和父类返回的类型一致,或者时其子类子类方法不能缩小父类方法的访问范围

5.4 练习

编写一个 Person 类,包括属性 / private (name、age),构造器、方法say (返回自我介绍的字符串)。

编写一个 Student 类,继承 Person 类,增加 id 、score 属性 / private ,以及构造器,定义 say 方法(返回自我介绍的信息)。

在 main 中,分别创建 Person 和 Student 对象,调用 say 方法输出自我介绍。

public class Person {
	private String name;
	private int age;
	//(编写一个Person类,包括属性/private (name、age),构造器、方法say(返回白我介绍的字符串)
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String say() {
		return "name = " + name + " age = " + age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge() {
		this.age = age;
	}
}
//编写一个Student类,继承Person类,增加id、score属性/private,
//以及构造器,定义say方法(返回自我介绍的信息).。
public class Student extends Person {
	private int id;
	private double score;

	//继承person,但是person已经没有定义默认构造器
	public Student(String name, int age, int id, double score) {
		super(name, age);	//调用父类构造器
		this.id = id;
		this.score = score;
	}

	//say
	public String say() {	//体现 super 的一个好处,代码复用
		return super.say() + " id = " + id + " score = " + score;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public double getScore() {
		return score;
	}
	
	public void setScore() {
		this.score = score;
	}
}
public class OverrideExercise {
	public static void main(String[] args) {
		//在main中,分别创建Person和Student对象,调用say方法输出自我介绍
		Person jack = new Person("jack", 10);
		System.out.println(jack.say());

		Student smith = new Student("smith", 20, 123456, 99.8);
		System.out.println(smith.say());
	}
}

GitHub代码
gitee代码

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值