面向对象三大特性
构造器引入
//类中有五大成员,且必需有构造器。
//若不显示声明任何构造器,则会系统默认提供空参构造器。
//若显示声明构造器,则系统不提供构造器。
//提供默认构造器可以通过javap命令反编译字节码文件,从而查看系统提供的空参构造器
class Person {
String name;
int age;
public void eat() {
System.out.println("干饭");
}
}
// 该类生成的字节码文件通过javap命令反编译后的结果,见下图
//构造器的用法(显示声明)
class Person {
String name;
int age;
public Person() {
//空参构造器
}
//一旦声明构造器(无论有无参数),系统都将不再提供默认的空参构造器 ---仍然可用javap反编译查看
public Person(String name, int age) {
//带参数的构造器,一般用于初始化属性
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("干饭");
}
}
1.封装
是一种思想,一种开发经验,私有化某些成员,通过提供公共的方法去访问或修改
//未封装
class Person {
String name;
int age;
}
Person p = new Person();
//可以直接访问属性,并修改
p.name = "张三";
System.out.println(p.name);
//封装(私有化属性,类外无法直接访问,提供公共的getXxx()和setXxx()方法去访问或修改)
class Person {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//属性age的get/set方法同上
}
Person p = new Person();
//p.name = "张三"; //不可直接访问或修改属性
p.setName("薛定猫的谔");
System.out.println(p.getName());
2.继承
问题引入:以下代码太过冗余,重复性高,代码健壮性不好,更不利于管理
class Person() {
String name;
int age;
int tall;
public void eat() {
System.out.println("人吃饭");
}
public void work() {
System.out.println("人要工作");
}
}
//***************************************
class Student() {
String name;
int age;
int tall;
double score; //学生有成绩
int id; //学生有学号
public void eat() {
System.out.println("人吃饭");
}
public void work() {
System.out.println("学生学习");
}
}
//***************************************
class Teacher() {
String name;
int age;
int tall;
double salary; //老师有工资
public void eat() {
System.out.println("人吃饭");
}
public void work() {
System.out.println("老师教学");
}
}
技术引入:继承 关键字(extends)
class A extends B {
//正常编写A类所特有成员
}
//A直接获取所有B中所有的成员(除构造器外)
class Person() {
String name;
int age;
int tall;
public void eat() {
System.out.println("人吃饭");
}
public void work() {
System.out.println("人要工作");
}
}
//***************************************
class Student() {
double score; //学生有成绩
int id; //学生有学号
public void work() {
System.out.println("学生学习");//对父类的方法重写
}
}
//***************************************
class Teacher() {
double salary; //老师有工资
public void work() {
System.out.println("老师教学");
}
}
继承的本质:建立类之间的查找关系(类加载时构建),默认从子类本身开始查找要访问的成员
1.查找(一旦查找到,不在查找) 2.访问权限(允许访问,访问成功,否则报错)
方法区:加载类信息(属性 方法)
访问方法时,从方法区加载类信息开始查找(默认从自身类信息开始访问)
堆:堆中创建对象时会创建多组属性,一组自身继承父类的属性,一组原本父类的属性
继承的核心:
1.继承的本质:建立类之间的查找关系(见上面内存图)
2.查找:就近查找(默认从子类开始 或 super.成员(从直接父类开始查询,super不能多级使用(super.super.成员)))
3.多态
问题引入
//需要继承Animal成为具体的动物并创建对象,继承Food成为具体的食物并创建对象,然后两个对象作为参数传递给下面方法
public void feed(Animal animal, Food food) { //简写(人"类"的喂食feed方法)
System.out.println("主人给" + animal.getName() + " 吃 " + food.getName());
}
多态技术引入
Person p = new Person();
Animal animal = new Cat();
Food food = new Fish();
p.feed(Animal animal, Food food)
animal = new Dog();
food = new bone();
p.p.feed(Animal animal, Food food)
多态的前提是封装和继承---重写、等
class Animal {
String name;
int age;
public void eat() {
System.out.println("进食");
}
}
Class Cat extends Animal{
//特有属性
Sring color = "薛定谔的猫";
public void eat() {
System.out.println("进食");
}
//特有方法
public void cry() {
System.out.println("进食");
}
}
//********************************
//java是先通过JDK中的工具 javac.exe编译源文件---> .class文件 ---> 工具java.exe运行程序解释执行
// 先编译 ---> 再运行
Animal cat = new Cat(); // 默认向上转型(自动) ---父类引用指向子类对象
animal 的编译类型是Animal
animal 的运行类型是Cat ----实际指向堆内存中的对象是Cat类的对象
结果就是:父类对象——>子类对象
1.访问成员编译器只认编译类型即父类中声明的所有成员
2.而实际真正访问的对象则是时子类中同名的成员(若是方法,则可能被子类重写)---就近查找,动态绑定
向下转型:由于编译类型决定了能访问的成员,所以如果想要访问子类特有的成员
让子类引用指向子类对象
Cat cat = (Cat) animal; //相当于 父类引用--->子类对象 ===> 子类引用--->子类对象
前提是animal运行类型(即所指向的对象)是Cat类型的对象 + if(animal instanceOf Cat) {Cat cat = (Cat) animal;}
现在是 Cat类型的引用---->Cat的对象
完全可以访问Cat对象的所有成员
// A instanceOf B; 对象A是否是这个类B或者是它的子类的一个实例(运行类型,一定是对象)
核心:
1.编译类型 和 运行类型(getClass()方法获取)
2.动态绑定---> 动态绑定体现了Java的继承与多态,在继承链中,JVM一直沿着继承链动态找到带有该方法的实现
3.对属性而言,运行时哪个对象调用属性,这个属性就是当前对象的属性,没有则沿继承链查找
核心两句
1.继承的本质是建立类之间的查找关系
2.分清编译类型和运行类型,就近查找 + 动态绑定