Java基础学习:10、封装和继承和super、方法重载、多态、动态绑定
封装:
1、概念:
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
2、意义:
只能通过规定的方法访问数据。
隐藏类的实例细节,方便修改和实现。
3、实现步骤:
public class Encapsulation {
public static void main(String[] args) {
Person person = new Person("张三", 200, 30000);
System.out.println(person.message());
}
}
class Person{
public String name;
private int age;
private double salary;
public Person() {
}
// 构造方法可以破解封装,那么如果既要实用构造器又要封装呢?
// 将set写在构造器里面
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
setName(name);
setAge(age);
setSalary(salary);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >=2 && name.length() <=6) {
this.name = name;
} else {
this.name = "张三";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 1 && age <= 150) {
this.age = age;
} else {
System.out.println("输入的年龄不对,需要在1~150之间");
this.age = 16; // 给一个默认的年龄
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String message() {
return "姓名为:" + name + " " + "年龄为:" + age + " " + "工资为:" + salary;
}
}
继承:
1、定义:继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。
2、继承意义:
子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用。
3、语法规则:只要在子类加上extends关键字继承相应的父类就可以了。
4、注意点:
a、子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,
但是私有的属性和方法不能再子类直接访问,要通过父类提供的公共方法区访问。
public class Student {
public int score1 = 100;
public int score2 = 80;
private int score3 = 90;
// 父亲提供一个public方法,返回score3
public int getScore3() {
return score3;
}
public void test1() {
System.out.println("学生正在考试数学");
}
public void test2() {
System.out.println("学生正在考试英语");
}
private void test3() {
System.out.println("学生正在考试语文");
}
// 父亲提供一个public方法,调用test03
public void callTest() {
test3();
}
}
b、子类必须调用父类的构造器,完成父类的初始化。
c、当创建子类对象时,不管使用子类哪个构造器,默认情况都会去调用父类的无参构造器。
如果父类没有提供无参构造器,则必须在子类的构造器中用supre去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译 不会通过。
d、如果希望指定去调用父类的某个构造器,则需要显示的调用一下:super(参数列表);
e、super在使用时,必须放在构造器第一行(super只能在构造器中使用)。
f、super()和this()都只能放在构造器第一行,因此两个方法不能共存在一个构造器中。
g、Java中所有类都是object类的子类。
h、父类构造器的调用不限于直接父类,将一直往上追溯到object类(顶级父类)。
i、子类最多只能继承一个父类,即Java中的单继承机制。如果要让A继承B类和C类呢?【A继承B,B继承C】。
j、不能滥用继承,子类和父类必须满足is-a的逻辑关系:如 猫是动物。
继承内存图:
继承中this和super:
public class ExtendsHomeWork {
public static void main(String[] args) {
C c = new C();
}
}
super关键字:
1、定义:super代表父类的引用,用于访问父类的属性、方法、构造器。
2、基本用法:
a、用于访问父类的属性,但不能访问父类的private属性:super.属性名;
b、用于访问父类的方法,但不能访问父类private方法:super.方法名(参数列表);
c、访问父类的构造器:super(参数列表); 只可以放在构造器第一句,只能出现一句。
3、意义及细节:
a、super调用父类构造器的意义(分工明确,父类的属性由父类初始化,子类的属性由子类初始化)。
class NotePad extends Computer {
private String color;
public NotePad(String cpu, int memory, int disk, String color) {
super(cpu, memory, disk);
this.color = color;
}
}
b、当子类和父类中的成员(属性和方法)重名时,为了访问父类成员,必须通过super,如果没有重名,则super、this以及直接访问都是一样的。
在子类中使用:直接访问、this、super三者的区别:
直接访问和this:
1、先找本类,本类有,则调用。
2、本类没有,找父类(如果有且可以调用,则调用;如果有但是不能调用,则报错)。
3、如果父类没有,则继续往上找,直到object类。一直没有找到,提示不存在。
super:
1、直接找父类,不找本类。其余规则如上。
class Computer {
private String cpu;
private int memory;
private int disk;
public int num = 12;
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
}
class NotePad extends Computer {
private String color;
public int num = 1;
public void test() {
System.out.println(num); // 1
System.out.println(this.num); // 1
System.out.println(super.num); // 12
}
}
c、super访问不限于直接父类,如果爷爷类也有同名成员,也可以使用super去访问。如果多个上级类都有同名成员,遵循就近原则。
继承方法重写:
1、定义:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。
2、满足条件:
a、子类的方法的形参列表、方法名称要和父类的参数方法名称完全一样。
b、子类方法的返回类型要和父类一样活着是父类返回类型的子类:如父类返回类型是object,子类是String。
c、子类方法不可以缩小父类方法的访问权限:public > protected > 默认 > private。
方法重写和方法重载的区别:
1、方法重载:Java中允许在同一个类中,多个同名方法的存在,到要求形参列表不一致。
2、方法重写:子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就是子类方法覆盖了父类方法。
多态:
一、多态概述:
1、多态是继封装、继承之后,面向对象的第三大特性。
2、多态现实意义理解:
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
3.多态体现为父类引用变量可以指向子类对象。
Animal animal = new Dog("米奇", 23);
4.前提条件:必须有子父类继承关系。
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
// 运行时,执行到该行的时候,animal运行类型是dog,所以是dog的eat();
animal.eat(); // 小狗吃东西
5.多态的定义与使用格式
定义格式:父类类型 变量名=new 子类类型();
6.理解:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
二、多态成员特点:
1、成员属性(变量):
属性没有重写之说,属性的值看编译类型:直接调用的就是animal的属性的值:
Animal animal = new Dog("米奇", 23); // 编译类型是animal
System.out.println(animal.num); // 父类的num
2、成员方法:
成员方法有重写,如果重写,调用的就是子类方法,无重写,调用父类:
animal.say(); // 子类重写父类say方法,调用的是子类
public static void main(String[] args) {
// animal 编译类型是 Animal,运行类型是 dog
Animal animal = new Dog("米奇", 23);
// 运行时,执行到该行的时候,animal运行类型是dog,所以是dog的eat();
// 如果子类没有重写父类的方法,那么调用的就是父类的方法
animal.AnimalEat(); // 父类的方法:动物吃东西
// 如果子类重写了父类的方法,那么调用的就是子类的方法。
animal.say(); // 子类的方法:小狗唱歌
System.out.println(animal.num); // 父类的值:10
}
三、多态的转型 :向上转型和向下转型
1、向上转型:
父类的引用指向了子类的对象(多态本身就是向上转型过的过程)
使用格式:父类类型 变量名=new 子类类型();
Animal animal = new Dog("米奇", 23);
特点:
a、可以调用父类的所有成员(变量和方法),但是需要遵循访问权限。
b、不能调用子类的特有成员,可以调用子类的重写方法:因为在编译阶段,能调用哪些成员,有编译类型决定(编译看左边)。
为什么不能调用子类成员?
因为编译的时候就不通过,编译就会报错。
总结:向上转型 父类类型 变量名=new 子类类型();
a、编译的时候只能调用父类方法。
Animal animal = new Dog("米奇", 23);
animal.AnimalEat(); // 父类方法
animal.DogEat(); // 子类方法报错
b、运行的时候还是先从子类找该方法,如果有就调用,没有往上找。
(就是如果子类重写了父类方法,运行的时候就会调用子类,没有重写就不会调用子类方法,调用父类方法)
// 子类重写了父类say方法:编译时调用的是父类方法,但是实际运行时调用的是子类重写的say方法
animal.say(); // 小狗唱歌
2、向下转型:
1、前提:如果要向下转型必须先向上转型:
Animal animal = new Dog;
2、语法:子类类型 引用名 = (子类类型) 父类引用;
Dog dog = (Dog) animal;
3、注意点:
a、只能强转父类引用,不能强转父类对象。
Dog dog = (Dog) Animal; // 错误
b、向下转型后,可以调用子类中所有成员。
dog.DogEat();
instanceof:比较操作符
1、意义:
1、用于判断对象的运行类型是否为某个类型 或者 为某类型的子类型。
2、它的作用是测试它左边的对象是否是它右边的类的实例
public class instanceOf {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
// aa的编译类型是AA,aa的运行类型是BB
AA aa = new BB();
// 下面这句话意思:aa的运行类型BB 是 AA 的类或者子类吗?
System.out.println(aa instanceof AA); // true
// 下面这句话意思:aa的运行类型BB 是 BB 的类或者子类吗?
System.out.println(aa instanceof BB); // true
}
}
class AA {}
class BB extends AA{}
2、实际用法:
在写程序的时候,如果要进行类型转换,我们最好使用instanceof运算符来判断
它左边的对象是否是它右边的类的实例,再进行强制转换。
动态绑定:
1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
public class DynamicBinding {
public static void main(String[] args) {
// father的编译类型:Father;father的运行类型:Son。
Father father = new Son();
// 调用方法时,会和运行类型绑定,cal方法会调用子类的sum方法。
System.out.println(father.cal()); // 40
}
}
class Father{
int num = 10;
public int cal() {
return sum() + 20;
}
public int sum() {
return num + 10;
}
}
class Son extends Father{
int num = 20;
public int sum() {
return num;
}
public int cal() {
return num + 20;
}
}
2、当调用对象属性的时候,没有动态绑定,哪里声明,哪里使用。
多态参数:
方法的形参类型为父类类型,实参为子类类型:
public class Main {
public static void main(String[] args) {
Ordinary tom = new Ordinary("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
Main main = new Main();
main.showEmpAnnual(tom);
main.showEmpAnnual(milan);
main.testWork(tom);
main.testWork(milan);
}
// 方法:输出普通员工和经理的工资
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
// 添加一个方法:如果是普通员工,则调用work方法,如果是经理,则调用manage方法
// 使用 类型判断 + 向下转型 这里 e 就是 Employee的引用类型 Employee e =
public void testWork(Employee e) {
if (e instanceof Ordinary) {
/*
下面这句相当于:
Ordinary ordinary = (Ordinary)e;
ordinary.work;
的简写
* */
((Ordinary) e).work(); // 向下转型
} else if (e instanceof Manager) {
((Manager) e).manage();
} else {
System.out.println("不处理");
}
}
}
toString方法:
1、他是object类下面的一个方法,因此所有的类都可以使用。
2、默认返回:全类名(包名 + 类名)+ @ + 十六进制哈希值。
3、当直接输出一个对象时,默认会调用tostring方法。
public class ToString {
public static void main(String[] args) {
AA aa = new AA();
System.out.println(aa.toString()); // com.homework.AA@776ec8df
System.out.println(aa); // com.homework.AA@776ec8df
}
}
class AA {}
4、子类往往会重写tostring方法,用于返回对象的属性信息
public class ToString {
public static void main(String[] args) {
AA aa = new AA("mary", 23);
System.out.println(aa.toString()); // AA{name='mary', age=23}
System.out.println(aa); // AA{name='mary', age=23}
}
}
class AA {
String name;
int age;
public AA(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "AA{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}