1. 继承性的练习

  2. Eclipse的Debug功能

  3. 方法重写

  4. 测试四种权限修饰符

  5. super关键字

  6. 子类对象实例化过程

  7. 继承和super的练习

  8. 多态

  9. 虚拟方法调用的再理解

 

1,继承性的练习

    ManKind类

 

package com.atguigu.java;

public class ManKind {
	private int sex;
	private 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() {
		String jobInfo = (salary == 0) ? "no job" : "job";
		System.out.println(jobInfo);
	}
	public int getSex() {
		return sex;
	}
	public void setSex(int sex) {
		this.sex = sex;
	}
	public int getSalary() {
		return salary;
	}
	public void setSalary(int salary) {
		this.salary = salary;
	}
}
    Kids类

 

package com.atguigu.java;

public class Kids extends ManKind {
	private int yearsOld;
	
	
	public Kids() {
	}
	public Kids(int yearsOld) {
		this.yearsOld = yearsOld;
	}

	public void printAge() {
		System.out.println("I am " + yearsOld + "year old");
	}

	public int getYearsOld() {
		return yearsOld;
	}

	public void setYearsOld(int yearsOld) {
		this.yearsOld = yearsOld;
	}
}
    test类

 

package com.atguigu.java;

public class KidsTest {
	public static void main(String[] args) {
		Kids someKid = new Kids(12);
		
		someKid.printAge();
		
		someKid.setSalary(0);
		someKid.setSex(1);
		
		someKid.employeed();
		someKid.manOrWoman();
	}
}
    Circle类

 

package com.atguigu.java;

public class Circle {
	private double radius;
	
	public Circle() {
		radius = 1.0;
	}

	public double getRadius() {
		return radius;
	}

	public void setRadius(double radius) {
		this.radius = radius;
	}
	
	public double findArea() {
		return Math.PI * radius * radius;
	}
}
    Cylinder类

 

package com.atguigu.java;

public class Cylinder extends Circle {
	private double length;
	
	public Cylinder() {
		length = 1.0;
	}

	public double getLength() {
		return length;
	}

	public void setLength(double length) {
		this.length = length;
	}
	
	public double findVolume() {
		return findArea() * getLength();
	}
}
    test类

 

package com.atguigu.java;

public class CylinderTest {
	public static void main(String[] args) {
		Cylinder cy = new Cylinder();
		
		cy.setRadius(2.1);
		cy.setLength(3.4);
		double volume = cy.findVolume();
		System.out.println("圆柱体积为:" + volume);
		
		double area = cy.findArea();
		System.out.println("底面积为:" + area);
	}
}

 

2,Eclipse的Debug功能

 

 

3,方法重写

    子类继承父类后,可以对父类中的方法进行再次定义,当通过子类对象调用这个同名同参的方法时,调用的就是子类中重写过的父类方法
    Person类

 

package com.atguigu.java;

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 walk(int distance) {
		System.out.println("走了" + distance + "公里");
		show();
		eat();
	}
	
	private void show() {
		System.out.println("我是一个人");
	}
	
	public Object info() {
		return null;
	}
	public double info1() {
		return 1.0;
	}
}
    Student类

 

package com.atguigu.java;

public class Student extends Person {
	String major;
	
	public Student() {
		
	}
	
	public Student(String major) {
		this.major = major;
	}
	
	public void study() {
		System.out.println("学习,专业是:" + major);
	}
	
	public void eat() { // 重写父类中的eat()方法
		System.out.println("学生应该多吃有营养的");
	}
	
	public void show() { // Eclipse中也没有重写方法提示(倒三角形),这不是一个重写方法,是子类自己定义的方法
		System.out.println("我是一个学生");
	}
	
	public String info() { // 返回一个Object的子类,构成重写
		return null;
	}
//	public int info1() { // 报错,父类的该方法在子类中可见,一个同名方法没有构成重载和重写
//		return 1;
//	}
	public double into1() {
		return 2.0;
	}
}
    测试

 

package com.atguigu.java;

/*
 * 方法声明:
 *     权限修饰符 返回值类型 方法名(形参列表) throws 异常类型 {
 *         // 方法体
 *     }
 * 重写的规定:
 *     子类重写的方法要与父类被重写方法的方法名和参数列表相同(方法名和参数列表确定唯一方法)
 *     子类重写方法的权限修饰符不小于父类被重写方法;父类中private的方法对于子类是不可见的,
 *         所以子类不能重写父类中声明为private的方法
 *     若父类被重写方法的返回值类型是void,则子类重写方法的返回值类型也是void
 *         若父类被重写方法返回A类型,则子类重写方法返回可以是A也可以是A的子类
 *         显然,若父类被重写方法返回基本数据类型,则子类重写方法返回相同的基本数据类型
 *     子类重写方法抛出的异常不能大于父类被重写方法抛出的异常类型
 *
 * static的方法不能被重写。当子父类中有同名同参的方法时,要么都声明为非static(在满足以上条件时
 *     就是重写),要么都声明为static(不是重写)
 * 在实际开发中,要重写方法时,直接将父类中被重写方法的定义复制到子类中,重写方法体即可
 */

public class PersonTest {
	public static void main(String[] args) {
		Student s = new Student("计科");
		s.eat();
		s.show(); // 父类的show()对于子类是不可见的,子类中的show()只是子类自己定义的,并不是重写的父类方法
		s.walk(10); // 子类中没有walk()方法,所以调用的是继承自父类中的,在父类中walk()又调用了父类自己的
					// show()和eat(),因为权限导致show()没有被重写,所以执行的是父类的show()、子类的eat()
		s.study();
		System.out.println("***********");
		Person p1 = new Person();
		p1.eat();
	}
}
    执行结果

 

day12-面向对象(中)_面向对象章节

 

4,测试四种权限修饰符

    父类Order

 

package com.atguigu.java1;

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;
	}
}
    子类SubOrder

 

package com.atguigu.java11;

import com.atguigu.java1.Order;

public class SubOrder extends Order {
	// 定义在不同包下的子类
	// 这里是定义属性的地方,可以声明属性并赋值,但不能这样
	// int i;
	// i = 1;
	// 所以不能在这直接测试orderPublic = 1;
	
	public void method() {
		orderProtected = 1; // 在不同包下的子类,protected的权限是可见的
		orderPublic = 2;
		// orderDefault = 1;
		// orderPrivate = 2; 不可见
	}
}

 

5,super关键字

    父类Person

 

package com.atguigu.java1;

public class Person {
	
	String name;
	int age;
	int id = 1001; // 身份证号
	
	public Person() {
		System.out.println("我无处不在");
	}
	
	public Person(String name) {
		this.name = name;
	}
	
	public Person(String name, int age) {
		this(name);
		this.age = age;
	}
	
	public void eat() {
		System.out.println("人吃饭");
	}
	
	public void walk() {
		System.out.println("人走路");
	}
}
    子类Student

 

package com.atguigu.java1;

/*
 * super理解为父类
 * 可以用来调用属性、方法、构造器
 * 调用属性和方法:
 *     在子类的方法或构造器中可以通过super.属性或super.方法的方式显示调用父类中的结构,
 *         通常情况下省略super
 *     当子父类中有同名的属性或方法时,通过super表明调用的就是父类中的结构
 * 调用构造器:
 *     在子类构造器中显示使用super(形参列表)来调用父类构造器
 *     super(形参列表)必须声明在子类构造器中的首行
 *     由于调用本类构造器的this(形参列表)也必须放在首行,所以二者只能二选一
 *     在子类构造器中如果既没有写this(形参列表)也没有super(形参列表),则默认调用父类的
 *         空参构造器super()。注意如果父类中写了带参的构造器而没有写空参构造器,此时是不会默认
 *         提供空参构造器的,所以在子类构造器中如果没有显示写出调用父类的带参构造器则会报错
 * 构造器的第一行不是super调用父类构造器就是this调用本来构造器;若一个类有多个构造器,则至少有
 *     一个调用了父类构造器,其他的则是通过this调用本类构造器,如果都调用this则会形成循环
 *     父类的构造器一定会被子类调用,Object是所有类的父类,它的构造器也一定会被子类调用;因为有了
 *         这样的机制,就保证了父类的结构会被加载到子类中
 */

public class Student extends Person {
	
	String major;
	int id = 1002; // 学号,开发中不会这样写。当创建当前对象时,堆空间中有两个id
	
	public Student() {
		// super(); // 没有显示的写出,其实默认也调用了父类的空参构造器
	}
	public Student(String major) {
		// super();
		this.major = major;
	}
	public Student(String name, int age, String major) {
//		this.name = name; // 在父类没有将属性声明为private时可以这样做
//		this.age = age; // 如果声明为private,也可以通过set()方法来设置,但一般不这样做
		super(name, age); // 调用父类构造器给属性赋值
		this.major = major;
	}
	
	public void eat() {
		System.out.println("学生多吃有营养的");
	}
	
	public void study() {
		System.out.println("学生学习知识");
		eat(); // 调用子类重写方法
		super.eat(); // 调用父类的被重写方法
		walk(); // this.walk(),先在本类中找,没找到就去父类中找,这里的父类可以是向上继承的所有父类。若是写成super.walk()则直接去父类中找
	}
	
	public void show() {
		System.out.println("name = " + this.name + "age = " + super.age); // 此时属性没有冲突,Student有自己的name和age属性,既可以说调用自己的,也可以调用父类的
		System.out.println("id = " + id); // 当子父类中都有时,默认调用子类自己的,相当于this.id
		System.out.println("id = " + super.id); // 调用父类的属性
							// 当使用this.属性时,明确指出先在当前子类中查找,有的话就用子类的,没有的话还是要去父类中找;而super.属性则是直接跳过子类,直接去父类中找
	}
}

 

6,子类对象实例化过程

    当创建一个子类对象时,从结果来看,该对象在堆空间中加载了所有父类中声明的所有属性和方法
    从过程来看,子类创建对象时,一定会直接或间接的调用父类的构造器,进而调用所有父类的构造器,直到java.lang.Object中的空参构造器为止,所以堆空间中才会有所有父类的属性和方法
    虽然创建子类对象的过程中调用了父类的构造器,但最终只创建了一个对象,即new的子类对象。这是不是间接说明不是通过构造器创建的对象?

 

 

7,继承和super的练习

    父类Account

 

package com.atguigu.java2;

public class Account {
	private int id;
	private double balance;
	private double annualInterestRate;
	
	public Account(int id, double balance, double annualInterestRate) {
		super();
		this.id = id;
		this.balance = balance;
		this.annualInterestRate = annualInterestRate;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	public double getAnnualInterestRate() {
		return annualInterestRate;
	}
	public void setAnnualInterestRate(double annualInterestRate) {
		this.annualInterestRate = annualInterestRate;
	}
	
	public double getMonthlyInterest() {
		return annualInterestRate / 12;
	}
	
	public void withdraw(double amount) {
		if(balance >= amount) {
			balance -= amount;
			return;
		}
		System.out.println("余额不足");
	}
	
	public void deposit(double amount) {
		if(amount > 0) {
			balance += amount;
		}
	}
}
    子类CheckAccount

 

package com.atguigu.java2;

public class CheckAccount extends Account {
	private double overdraft; // 可透支限额
	
	public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
		super(id, balance, annualInterestRate);
		this.overdraft = overdraft;
	}
	
	public void withdraw(double amount) {
		if(getBalance() >= amount) {
			super.withdraw(amount);
		}else if(overdraft >= amount - getBalance()) {
			overdraft -= (amount - getBalance());
			super.withdraw(getBalance());
		}else {
			System.out.println("超出透支额度");
		}
	}
}

 

8,多态

    父类Person

 

package com.atguigu.java3;

public class Person {
	String name;
	int age;
	
	public void eat() {
		System.out.println("人:吃饭");
	}
	
	public void walk() {
		System.out.println("人:走路");
	}
}
    子类Man

 

package com.atguigu.java3;

public class Man extends Person {
	boolean isSmoking;
	
	public void earnMoney() {
		System.out.println("男人负责挣钱养家");
	}
	
	public void eat() {
		System.out.println("多吃肉");
	}
	
	public void walk() {
		System.out.println("潇洒地走路");
	}
}
    子类Woman

 

package com.atguigu.java3;

public class Woman extends Person {
	boolean isBeauty;
	
	public void goShopping() {
		System.out.println("女人喜欢购物");
	}
	
	public void eat() {
		System.out.println("减肥");
	}
	
	public void walk() {
		System.out.println("妖娆地走路");
	}
}
    测试说明

 

package com.atguigu.java3;

/*
 * 多态性:父类引用指向子类对象
 * 使用:虚拟方法调用
 *     在编译期间,只能调用父类中声明的方法,而在运行期间,实际调用的是子类中重写的方法
 *     编译看左边(引用类型),运行看右边(对象实际类型)
 * 使用前提:
 *     要有类的继承关系;子类中重写了父类方法
 */

public class PersonTest {
	
	public static void main(String[] args) {
		Person p1 = new Person();
		p1.eat();
		
		Man man = new Man();
		man.eat();
		man.age = 25;
		man.earnMoney();
		
		Person p2 = new Man(); // 对象的多态性:父类的引用指向子类的对象
		p2.eat(); // 调用的是子类中重写过的方法——虚拟方法调用。编译时发现Person类型有eat()方法,所以不报错,运行时发现eat()方法被重写了,所以执行的是Man重写的方法
		// p2.earnMoney(); // 编译不通过,Person类型中没有这个方法
		
		Person p3 = new Woman();
		p3.eat(); // 调用的是子类Woman重写的方法
	}
}
    举例一:

 

package com.atguigu.java3;

public class AnimalTest {
	
	public static void main(String[] args) {
		AnimalTest test = new AnimalTest();
		test.func(new Dog()); // 给一个animal的引用赋值一个Dog对象的引用,当调用eat()时,
							// 编译发现animal有这个方法,通过,执行时发现这其实是一个Dog
							// 的对象,所以执行Dog的eat()方法
	}
	
	public void func(Animal animal) {
		animal.eat();
		animal.shout();
	}
	
//	public void func(Dog dog) { // 有了多态性后,就不用写以下的重载方法了
//		dog.eat();
//		dog.shout();
//	}
//	public void func(Cat cat) {
//		cat.eat();
//		cat.shout();
//	}
}

class Animal {
	public void eat() {
		System.out.println("动物:进食");
	}
	
	public void shout() {
		System.out.println("动物:叫");
	}
}

class Dog extends Animal {
	public void eat() {
		System.out.println("狗吃骨头");
	}
	
	public void shout() {
		System.out.println("汪汪汪");
	}
}

class Cat extends Animal {
	public void eat() {
		System.out.println("猫吃鱼");
	}
	
	public void shout() {
		System.out.println("喵喵喵");
	}
}
    举例二:

 

package com.atguigu.java3;

import java.sql.Connection;

public class SqlTest {
	
	public void doData(Connection conn) { // 可以传入new MySQLConnection();或是new OracleConnection();的对象
		// 规范的步骤去操作数据,不管传入的是哪个子类对象,子类中都重写了以下的方法
		// conn.method1();
		// conn.method2();
		// conn.method3();
	}
}
    多态性只适用于方法,不应用于属性

 

package com.atguigu.java3;

public class Test {
	public static void main(String[] args) {
		Test1 t = new Test2();
		System.out.println(t.id); // 1001,此时编译运行都看左边,调用的是Test1类型的属性
	}
}

class Test1 {
	int id = 1001;
}

class Test2 extends Test1 {
	int id = 1002;
}

 

9,虚拟方法调用的再理解

    在多态情况下,子类重写了父类的某些方法,此时父类的这些方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法,这样的方法调用在编译期是无法确定的
    引用变量是父类类型,而在运行时方法调用的是某个子类的方法,这种行为称为动态绑定
    多态是一个编译时行为还是运行时行为?

 

package com.atguigu.java2;

import java.util.Random;

public class InterviewTest {
	
	public static Animal getInstance(int key) {
		switch(key) {
		case 0:
			return new Cat();
		case 1:
			return new Dog();
		default:
			return new Sheep();
		}
	}
	
	public static void main(String[] args) {
		int key = new Random().nextInt(3);
		System.out.println(key);
		Animal animal = getInstance(key); // key是一个随机数,在编译期时无法知道创建的是哪个对象,只有在运行时确定
		animal.eat();
	}
}

class Animal {
	
	protected void eat() {
		System.out.println("animal eat food");
	}
}

class Cat extends Animal {
	protected void eat() {
		System.out.println("cat eat fish");
	}
}

class Dog extends Animal {
	public void eat() {
		System.out.println("Dog eat bone");
	}
}

class Sheep extends Animal {
	public void eat() {
		System.out.println("sheep eat grass");
	}
}
    经典面试题:重载和重写的区别
    定义:重载是指在一个类中声明了多个同名不同参的方法,通过形参的不同来区别和调用这些方法。重写是指在子类中重新声明了父类中的同名同参方法,将父类中方法的方法体进行了重写,在通过子类对象调用这个方法时,调用的是子类中这个重写的方法
    从编译和运行的角度看:重载时编译器根据同名方法的不同参数进行区分,它们也就成了不同的方法,它们的调用地址在编译期就已经绑定了,所以编译器在方法被调用之前就确定了要调用的是哪个方法,这称为早绑定或静态绑定;重载可以是子父类的关系,即子类中定义了一个与父类同名不同参的方法也构成重载。而对于重写,在多态的情况下,只有在方法被调用的那一刻,解释运行器才知道要调用的是哪一个方法,这称为晚绑定或动态绑定;若不是多态的一般情况下,编译器明确知道它要调用的是哪个方法,所以应该也是编译时行为
    重写与多态有关,重载与多态无关