概述
原则
好处
继承
是面向对象三大特征之一。
可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。
格式:
public class 子类 extends 父类{}
理解:
案例:
// 119
//测试类
public class Demo {
public static void main(String[] args){
//创建Fu对象
Fu f = new Fu();
f.show();
//创建Zi对象
Zi z = new Zi();
z.mothod();
z.show();//此方法是Fu类中的。
}
}
//子类
public class Zi extends Fu {
public static void mothod(){
System.out.println("mothod方法被调用");
}
}
//父类
public class Fu {
public static void show(){
System.out.println("show方法被调用");
}
}
①继承体现的关系:is a
②假设法:有两个类A和B,如果他们满足A是B的一种,或者B是
A的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承来体现,否则就不能滥用继承。
在子类方法中访问一个变量:
子类局部范围找(子类方法中)—》
子类成员范围找(子类中)—》
父类成员范围找(父类中)—》
如果都没有就报错(不考虑父亲的父亲...)
案例:
// 119
//测试类
public class Demo1 {
public static void main(String[] args){
//创建对象
Zi1 z1 = new Zi1();
z1.show();
}
}
//子类
public class Zi1 extends Fu1 {
//身高
public int height = 175;
public int age = 20;
public void show(){
int age = 10;
System.out.println(age);
System.out.println(height);
}
}
//父类
public class Fu1 {
public int age = 40;
}
分析
前提:创建子类对象,调用子类对象的show方法,其中有对变量height、age变量的输出。
情况1:
子类中有height=175、show方法;
父类中有age=40;
输出过程:
变量age show方法→子类成员→父类成员→40
变量height show方法→子类成员→175
情况2:
子类中有height=175 age = 20、show方法;
父类中有age=40;
输出过程:
变量age show方法→子类成员→20
变量height show方法→子类成员→175
情况3:
子类中有height=175 age = 20、show方法有age = 10;
父类中有age=40;
输出过程:
变量age show方法→10
变量height show方法→子类成员→175
构造方法的访问顺序
子类无参构造方法调用:
不写super:父类无参构造方法→子类无参构造方法
写super:父类的super()构造方法→子类无参构造方法
子类有参构造方法调用:
不写super:父类无参构造方法→子类有参构造方法
写super:父类的super()构造方法→子类有参构造方法
子类中所有的构造方法默认都会访问父类中无参的构造方法
如果父类中没有无参构造方法,只有带参构造方法,子类有参、无参构造方法报错的时候,解决方式:
案例:
// 119
//测试类
public class Demo3 {
public static void main(String[] args){
//无参构造方法创建子类对象
Zi3 z = new Zi3();
//输出结果是:
//父类中无参构造方法被调用 子类中无参构造方法被调用
1 通过子类的无参构造方法创建对象,父类中的无参构造对象方法也会被调用
Zi3 z1 = new Zi3(40);
//输出结果是:
// 父类中无参构造方法被调用 子类中有参构造方法被调用
2 通过子类带参数的构造方法创建对象,父类中的无参构造对象方法同样会被调用。
//情况一:当父类中没有无参构造方法的时候,子类中的无参构造方法、有参构造方法会报错;
//情况二:为保证情况一中子类不报错,使用super(参数);显示调用父类带参数的构造方法;
}
}
//父类
public class Fu3 {
public Fu3(){
System.out.println("父类中无参构造方法被调用");
}
public Fu3(int age){
System.out.println("父类中有参构造方法被调用");
}
}
//子类
public class Zi3 extends Fu3 {
public Zi3(){
System.out.println("子类中无参构造方法被调用");
}
public Zi3(int age){
System.out.println("子类中有参构造方法被调用");
}
}
通过子类对象访问一个方法:
子类成员范围找 =》
父类成员范围找 =》
如果没有就报错
案例:
// 122
//父类
public class Fu {
public void show(){
System.out.println("Fu类中show方法被调用");
}
}
//子类
public class Zi extends Fu{
public void method(){
System.out.println("Zi类中method方法被调用");
}
public void show(){
super.show();//表示访问父类的成员方法
System.out.println("Zi类中的show方法被调用");
}
}
//测试类
public class Demo {
public static void main(String[] args){
//创建对象
Zi z = new Zi();
z.method();
z.show();
// 通过子类创建对象,子类方法method、父类方法show
1 调用子类方法可以执行,调用父类方法同样可以执行
// 通过子类创建对象,子类方法method、show,子类show方法中没有super.show();父类方法show
2 调用show方法,首先会在子类中寻找,有则执行;没有则去父类中寻找
//在子类的show方法中添加super.show()
3 调用show方法,执行父类中的show方法,再执行子类中的show方法。
}
}
理解:
情况①
父类方法show 子类方法method
子类实例化对象
对象.method可以执行(子类);对象.show可以执行(父类)
情况②
父类方法show 子类方法method、show
子类实例化对象
对象.method可执行(子类);对象.show可执行(子类)
情况③
父类方法show 子类方法method、show
并且在show方法中添加super.show()表示访问父类成员方法
子类实例化对象
对象.method可执行(子类);
对象.show执行:显示父类中的show方法,再是子类中的show方法
错误:
public class Son extends Father,Mother{}
正确:
public class Son extends Father{}
public class Granddad{}
public class Father extends Granddad{}
public class Son extends Father{}
这样就相当于Son类继承了Grandad类和Father类,
因为Father类继承了Granddad类
this关键字
指向调用该方法的对象,一般我们是在当前类中使用this关键字,所以我们常说this代表本类对象的引用。
super关键字和this关键字对比
描述 | this | super |
---|---|---|
定义 | 代表本类对象的引用 | 代表父类存储空间的标识 (可以理解为父类对象引用) |
访问成员变量 | this.成员变量 访问本类成员变量 |
super.成员变量 访问父类成员变量 |
访问构造方法 | this(…) 访问本类构造方法 |
super(…) 访问父类构造方法 |
访问成员方法 | this.成员方法(…) 访问本类的成员方法 |
super.成员方法(…) 访问父类成员方法 |
案例:
// 119
// 测试类
public class Demo2 {
public static void main(String[] args){
//创建对象
Zi2 z = new Zi2();
z.show();
//age变量有三个,父类中age = 40;子类中成员变量age=20;子类中方法show中的变量age=30;
// age表示输出的是 子类方法中的局部变量age
// this.age 表示输出的是 子类的成员变量age
// super.age 表示输出的是 父类的成员变量age
}
}
//子类
public class Zi2 extends Fu2 {
public int age = 20;
public void show(){
int age = 30;
//方法中age表示方法show中的变量
System.out.println(age);
//输出成员变量age,需要使用this.age
System.out.println(this.age);
//输出父类中的成员变量age,需要使用super.age
System.out.println(super.age);
}
}
//父类
public class Fu2 {
public int age = 40;
}
概念:
子类中出现了和父类中一模一样的方法声明,又被称为覆盖。
应用 :
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
@Override
是一个注解,可以帮助检查重写方法的方法声明是否正确性。
案例:
// 123
// 父类
public class Phone {
public void call(String name){
System.out.println("给" + name + "打电话");
}
}
//新手机
public class NewPhone extends Phone{
public void call(String name){
System.out.println("打开视频");
super.call(name);
}
}
//测试类
public class Demo {
public static void main(String[] args){
//创建对象,
Phone p = new Phone();
//创建类 都会默认有一个无参构造方法。
// 如果没有定义有参构造方法,系统会默认给出一个无参构造方法;
// 如果定义了有参构造方法,系统将不会给出无参构造方法。
p.call("汪苏泷");
System.out.println("-------------");
NewPhone np = new NewPhone();
np.call("许嵩");
}
}
理解:
父类Phone,子类NewPhone
①父类方法 call,子类 什么都不写
——对象.call 可以执行,方法调用当子类没有时,会自动去父类找;
②父类方法 call,子类重写的call方法,加入父类中call方法没有的东西,
——要想使用父类call方法的内容,可以使用super.call()访问父类成员方法call
③子类重写父类方法的时候,可能会出现子类重写方法名和父类要重写方法名 不一致的错误,可以使用注释@Override,如果子类父类方法名不一致@Override会报错。
私有方法:
private void call(){}
public:
public void call(){}
默认:
void call(){}
私有:
private void call(){}
理解
java子类重写方法的访问权限不能低于父类中的权限。
需求:
定义老师类和学生类,然后写测试代码;
最后找到老师类和学生类当中共的内容,抽取一个父类,用继承的方式改写代码,并进行测试。
// 123
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Teacher1 extends Person {
//定义带参构造方法
public Teacher1(String name,int age) {
// this.name = name;
报错1:
// 这种形式是会报错的,因为此类中并没有name、age变量,且父类中的两个变量是私有的。
//要想使用 需要调用父类中的有参构造方法。
super(name,age);
}
报错2:
//如果一个方法中定义了有参构造方法,则系统就不会给出默认的无参构造方法,此时测试类使用无参构造方法创建变量,会报错,需要手动创建无参构造方法。
public Teacher1(){}
public void teach(){
System.out.println("再小的努力,乘以365也很巨大");
}
}
public class PersonDemo {
public static void main(String[] args) {
Teacher1 t = new Teacher1();
t.setName("刘禹锡");
t.setAge(120);
System.out.println(t.getName() + ", " + t.getAge());
t.teach();
Teacher1 t2 = new Teacher1("陶渊明",120);
System.out.println(t2.getName() + ", "+t2.getAge());
t2.teach();
}
}
// 123
public class Animal {
//动物类
//成员方法
private String name;
private int age;
//无参、有参构造方法
public Animal(){
}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
// 成员变量的get/set方法
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
public class Cat extends Animal {
//创建无参有参构造方法
public Cat(){}
public Cat(String name,int age){
//通过super访问父类中的有参构造方法
super(name,age);
}
//成员方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
public class AnimalDemo {
public static void main(String[] args) {
Cat c = new Cat();
c.setAge(6);
c.setName("茶杯猫");
System.out.println(c.getName() + ", " + c.getAge());
c.catchMouse();
Cat c2 = new Cat("波斯猫",1);
System.out.println(c2.getName() + ", "+c2.getAge());
c2.catchMouse();
}
}
概念:
同一个对象,在不同时刻表现出来的不同形态 。
实现前提:
①继承/实现关系(父类子类/接口实现类)
②方法重写
③父类引用指向子类对象
理解:
普通创建对象:
类名 对象名 = new 类名();
猫 cat = new 猫();
多态:
父类类名 对象名 = new 子类类名();
动物 animal = new 猫();
父类引用指向子类对象
猫在不同的时刻表现出来了不同的形态,这就是多态。
更新内容:
案例:
// 126-test1
//父类
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
// 多态 前提① 继承关系——Cat继承于Animal
public class Cat extends Animal {
// 多态 前提② 方法重写——Cat类 重写父类Animal中的eat方法
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
// 测试类
public class CatDemo {
public static void main(String[] args) {
//多态 前提③:父类引用 指向子类的对象
Animal cat = new Cat();
// 父类类名 对象名 = new 子类类名();
}
}
多态:
形式:具体类多态、抽象类多态、接口多态
1 具体类多态
public class 子类 extends 父类{}
父类 对象名 = new 子类();
2 抽象类多态
public abstract class 父类{}
// 形式1 重写父类中所有的抽象方法;
public class 子类 extends 父类{
@Override
重写父类中所有的抽象方法;
}
// 形式2 子类也定义为抽象类
public abstract class 子类 extends 父类{}
父类 对象名 = new 子类();
3 接口多态
public interface 接口名{}
public class 接口实现类名 implements 接口名{}
接口名 对象名 = new 接口实现类名();
访问对象 | 说明 |
---|---|
成员变量 | 编译看左边,执行看左边 |
成员方法 | 编译看左边,执行看右边 |
成员变量和成员方法访问不一样的原因: 成员方法有重写,成员变量没有
理解:
成员变量:编译看左边,执行看左边
父类 对象名 = new 子类名();
对象名.成员变量
代码书写的时候,
对象名.成员变量,编译要看此成员变量是否在父类中定义,
定义-代码不报错,没有定义-代码报错。
对象名.成员变量,执行输出的具体值要看
父类中成员变量的具体值是多少,输出就是多少。
成员方法:编译看左边,执行看右边
对象名.成员方法
代码书写的时候,
对象名.成员方法,编译要看成员方法在父类中是否有定义,
定义-代码不报错,没有定义-代码报错。
对象名.成员方法,执行要看子类中对此成员方法的具体执行过程。
案例:
// 126-test2
public class Animal {
public int age = 40;
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
public int age = 20;
public int weight = 40;
@Override
public void eat(){
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("猫爱捉迷藏");
}
}
public class Demo {
public static void main(String[] args) {
//多态类型创建对象,对象.成员变量:编译要看左侧、运行也要看左侧
Animal cat = new Cat();
System.out.println(cat.age);
//输出 40
// 父类Animal age = 40,子类Cat age = 20
// 编译执行都在父类。
// System.out.println(cat.weight);
// 父类Animal中没有对象weight,所以对象.weight书写报错
// 多态类型创建对象,对象.成员方法():编译看左边,执行看右侧,因为在子类/接口具体实现类中,可能重写了成员方法。
cat.eat();
//输出 猫吃鱼 父类Animal中eat方法 是 动物吃东西;子类Cat中eat方法 是 猫吃鱼。
}
}
理解
多态形式创建对象:父类 obj = new 子类();
obj.成员变量;
子类继承父类的情况,成员变量的访问,
编译执行都在左边父类中的成员变量的值。
obj.成员方法();
子类继承父类的情况,成员方法的访问;
编译看左侧(父类)、执行看右侧(子类);
好处: 提高了程序的可扩展性
定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作;
弊端: 不能使用子类的特有功能
因为obj.成员方法,编译的时候会看父类是否含有此方法,作为子类的特有功能,父类是不会存在的,所以编译不通过。
案例:
// 126-test3 证明多态提高了程序的可扩展性。
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
@Override
//重写了父类Animal中的eat方法
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal {
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
// 动物类操作方法
public class AnimalOperator {
/* public void useAnimal(Cat c){
c.eat();
}
//再添加一个类,并在这里重写useAnimal方法
public void useAnimal(Dog d){
d.eat();
}*/
当添加x个Animal类的子类时,这里就需要重写useAnimal方法x次。
解决方式就是 useAnimal方法使用的参数是Animal类
public void useAnimal(Animal a){
//AnimalOperator对象.useAnimal(Cat c/Dog d)相当于
//Animal a = new Cat() / Animal a = new Dog() 也就是多态的形式
a.eat();
}
}
// 测试类
public class AnimalDemo {
public static void main(String[] args) {
//创建AnimalOperator类的对象
AnimalOperator a = new AnimalOperator();
Cat ao = new Cat();
//想调用useAnimal方法 需要创建Cat对象
a.useAnimal(ao);//输出 猫吃鱼
Dog d = new Dog();
a.useAnimal(d);//输出 狗吃骨头
}
}
类别 | 向上转型 | 向下转型 |
---|---|---|
说明 | 从子到父 父类引用指向子类对象 |
从父到子 父类引用转为子类对象 |
代码 | 父类 对象名 = new 子类名() | 父类 obj对象名 = new 子类名() 子类名 子类对象名 = (子类名) obj对象名 |
作用 | 用于解决一个类中多个方法重写问题 方法参数是引用数据类型,且继承于同一个类 多个子类重写修改:子类 变量名–》父类 变量名 |
用于解决通过多态方式创建对象,不能访问子类中特有方法的问题 |
案例:
// 126-test4
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
@Override
public void eat(){
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("猫爱玩捉迷藏");
}
}
public class AnimalDemo {
public static void main(String[] args) {
//向上转型
Animal a = new Cat();
a.eat();
// a.playGame();
// 编译报错,因为多态方式创建对象,对象调用方法:编译看左边,执行看右边,Animal类中没有playGame方法,此方法在Cat类中,所以直接编译报错。
//要先实现playGame方法调用
System.out.println("-----------");
//向下转型
//用于解决多态创建对象,不能访问子类对象特有方法的问题。
// 子类 子类对象名 = (子类)父类对象名;
Cat c = (Cat) a;
//将Animal类型的对象 强制转换为Cat类型的对象
c.eat();
c.playGame();
}
}
注意:
通过多态创建对象,子类继承父类,子类中的带参构造方法创建所需要的参数是父类中定义时,需要使用super(参数,参数)的形式进行创建。
// 126-test5
//父类
public class Animal {
//创建成员变量
public String name;
public int age;
//创建无参/有参构造方法,只要创建有参构造方法,系统将不会给出无参构造方法;也就是假如只给出有参构造方法,main方法中创建对象就不能使用无参构造方法创建了。
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
//成员变量的get/set方法
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
//成员方法
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
public Cat(){
}
// 子类的带参构造方法,所使用的参数是父类的参数,
// 所以需要使用super(name,age)实现参数的初始化。
public Cat(String name,int age){
super(name,age);
}
//重写父类方法
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
public Dog(){
}
public Dog(String name,int age) {
super(name,age);
}
@Override
public void eat(){
System.out.println("狗吃骨头");
}
}
public class AnimalDemo {
public static void main(String[] args){
//多态形式创建Cat类对象
Animal c = new Cat();
c.setName("加菲");
c.setAge(5);
System.out.println(c.getName() + ", " + c.getAge());
c.eat();
System.out.println("------------");
c = new Cat("波斯",2);
System.out.println(c.getName() + ", "+c.getAge());
c.eat();
}
}
定义:
万物皆对象,客观存在的事物皆是对象。
是能够看得到摸得着的真实存在的实体。
Java是从C++语言改进重新设计。
对象的属性:
对象具有的各种特征,每个对象的每个属性都拥有特定的值。
对象的行为:
对象能执行的操作。
备注2022/8/11
Java 语言的方法:
Java语言中的方法必定隶属于某一类(对象),调用方法与过程或函数相同。√
Java面向对象中允许单独的过程与函数存在×(过程和函数 不能单独存在)
Java面向对象语言中允许单独的方法存在×(方法不能单独存在)
Java语言中的方法属于类的成员×(方法属于类)
类属性调用(private、default、static):
类的实例对象 不可以调用 private修饰属性。
类.static修饰变量;this.static修饰变量;
对象和类之间的关系:
创建对象
类名 对象名 = new 类名();
使用对象
// 使用成员对象
格式:对象名.变量名
// 使用成员方法
格式:对象名.方法名()
案例:
需要在phoneDemo类的所在包下,创建phone类。
// 110
public class phone {
//编写类的成员变量
String brand;
int price;
//成员方法
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发短信");
}
}
public class phoneDemo {
public static void main(String[] args) {
//创建对象
phone p = new phone();
//使用对象
//使用对象的成员变量
System.out.println(p.price);//输出0
//类phone的成员变量price 定义 int类型,其对应的默认值是0
System.out.println(p.brand);//输出null
//类phone的成员变量brand 定义 string类型,其对应的默认值是null
//在没有定义具体值的时候,输出的是变量的默认值。
//给变量赋值再输出
p.price = 2999;
p.brand = "小米";
System.out.println(p.price);
System.out.println(p.brand);
//使用对象的成员方法
p.call();
p.sendMessage();
}
}
案例:学生
思路:
①定义一个学生类(Student)
成员变量:name、age
成员方法:study();
doHomework();
②定义一个学生测试类(StudentDemo)
要做测试,有主方法 = main方法
③在学生测试类中通过创建对象完成对成员变量和成员方法的调用
给成员变量赋值,输出成员变量的值
调用成员方法
对象内存图(单个对象)
图示:
代码:
学生类的创建以及测试代码。
// 110
//定义学生类
public class Student{
// 定义成员变量
String name;
int age;
//定义成员方法
public void study(){
System.out.println("学习");
}
public void doHomework(){
System.out.println("做作业");
}
}
//写测试类,要注意在main方法里写对象的创建和使用。
public class StudentDemo {
public static void main(String[] args){
//创建对象
Student stu = new Student();
System.out.println(stu);//com.itheima110.Student@1b6d3586
//使用成员对象
System.out.println(stu.name);//null
System.out.println(stu.age);//0
//对成员对象赋值
stu.name = "xhj";
stu.age = 25;
System.out.println(stu.name);//xhj
System.out.println(stu.age);//25
//使用成员
stu.study();//学习
stu.doHomework();//做作业
}
}
对象内存图(多个对象)
图示:
2022/6/29
同一类实例化的对象,对象属性不同,对象方法相同;
同一类实例化的对象,在堆内存开辟的存储空间不同;
代码:
// 110
public class StudentDemo2 {
public static void main(String[] args){
//创建第一个对象
Student stu1 = new Student();
stu1.age = 30;
stu1.name = "汐沫";
System.out.println(stu1);
//com.itheima110.Student@1b6d3586
System.out.println(stu1.age + ", " + stu1.name );
// 输出30, 汐沫
stu1.study();//学习
stu1.doHomework();//做作业
//创建第二个对象
Student stu2 = new Student();
stu2.age = 20;
stu2.name = "西贝";
System.out.println(stu2);
//com.itheima110.Student@4554617c
System.out.println(stu2.age + ", " + stu2.name );
//输出20, 西贝
stu2.study();//学习
stu2.doHomework();//做作业
}
}
多个对象指向相同的内存区域,一个对象修改成员属性,则另一个对象的成员属性也会被修改。
代码:
// 110
//多个内存指向相同内存图
public class StudentDemo3 {
public static void main(String[] args){
//创建对象1
Student stu1 = new Student();
stu1.age = 33;
stu1.name = "汪苏泷";
System.out.println(stu1);
//com.itheima110.Student@1b6d3586
System.out.println(stu1.name + ", " + stu1.age);
//汪苏泷, 33
//创建对象2
Student stu2 = stu1;
stu2.age = 36;
stu2.name = "许嵩";
System.out.println("stu2:" + stu2);
//stu2:com.itheima110.Student@1b6d3586
System.out.println("stu1:"+stu1);
//stu1:com.itheima110.Student@1b6d3586
System.out.println("stu2:"+stu2.name + ", " + stu2.age);
//stu2:许嵩, 36
System.out.println("stu1:"+stu1.name + ", " + stu1.age);
//stu1:许嵩, 36
}
}
一个源文件只能有一个public类。(源文件 = java文件)
一个源文件可以有多个非public类。
源文件的名称应该和public类的类名保持一致。
如果一个类定义在某个包中,那么package语句应该在源文件的首行。
如果源文件包含import语句,应该放在package和类定义之间。如果没有package语句,应该在源文件在最前面。
import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。