Java学习_day10_(继承、方法重写、继承中成员方法的访问问题、final关键字)

一.继承

继承:将多个类的共性内容抽取在一个独立的类中,然后独立的类和这些类产生一种“继承”关系. extends
(private私有的变量不能被直接继承,子类无法直接访问,可以通过公共方法间接访问)

Java提供的"继承" 特征 的好处:
1)提高了代码的复用性
2)提高了的代码的维护性
3)让类与类之间产生关系 ----- 是 “多态的前提条件”

Java开发原则:
开闭原则(经常使用到: 对修改关闭,对扩展开放)
迪米特原则
接口分离原则
依赖注入原则

开发中,设计原则: 都要遵循 ,"低耦合,高内聚"
耦合:类与类的产生的关系
低耦合:这种关系越少越好! “耦合”----只能避免,不能彻底解决!(Spring框架:IOC 控制反转)

内聚:执行某件事情的能力
高内聚:在一个类中能够完成的事情,不要使用多个类完成事情!

注意事项:

  1. 子类继承父类,只能继承父类非私有的成员,私有的成员,外界不能访问的,只能间接通过
    公共方法访问!

    子类中的功能:一般都是一些子类的特有功能

  2. 不要为了使用部分功能而使用继承!
    如果A类是B类的一种,或者B类是A类的一种,这个时候使用继承;
    继承的体现出的是一种“is a”的关系:xxx是xxx的一种

//父类
class Father{
	
	private int num = 20 ; // num 在 Father 中是 private 访问控制
	public int num2 = 30 ;
	
	public void show(){
		System.out.println( num) ;
		function() ;
	}
	
	private void function(){
		System.out.println("function father...") ;
	}
}


//子类
class Son extends Father{
	
	public void playGame(){
		System.out.println("玩游戏...") ;
	}
}



//测试类
class ExtendsDemo2{
	public static void main(String[] args){
		//创建子类对象
		Son s = new Son() ;
		//System.out.println(s.num) ;
		System.out.println(s.num2) ;
		s.show() ;	
		//s.function() ;
		s.playGame() ;
	}
}

演示代码:

//使用继承的思想:将Student类和Worker类的共性共性内容:抽取到一个Person类中
class Person{ //"父类"
	private String name ;
	private int age ;
	
	public Person(){}
	
	//构造方法赋值
	public Person(String name,int age){
		this.name = name ;
		this.age = age ;
	}
	
	//setXXX(xxx)
	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("饿了就需要eat") ;
	}
	public void sleep(){
		System.out.println("累了就需要sleep") ;
	}
}

//学生类和工人类 (子类)
class  Student extends Person{}
class  Worker extends Person{}

//测试类
class ExtendsDemo{

	public static void main(String[] args){
		//没有使用继承之前
		//创建一个学生类
		/*
		Student s = new Student("高圆圆",41) ;
		System.out.println(s.getName()+"---"+s.getAge()) ;
		s.eat() ;
		s.sleep() ;
		System.out.println("---------------------------") ;
		
		//创建工人类
		Worker w = new Worker("程序员",23) ;
		System.out.println(w.getName()+"---"+w.getAge()) ;
		w.eat() ;
		w.sleep() ;
		*/
		//使用继承了
		//方式:通过无参+setXXX()
		Student s = new Student() ;
		s.setName("高圆圆") ; 
		s.setAge(41)  ;
		System.out.println(s.getName()+"---"+s.getAge()) ;
		s.eat() ;
		s.sleep() ;
		
		
		Worker w = new Worker() ;
		w.setName("雷军") ;
		w.setAge(50) ;
		System.out.println(w.getName()+"---"+w.getAge()) ;
		w.eat() ;
		w.sleep() ;		
	}
}

继承的特点:

  1. 在Java中,类与类之间的继承关系,只支持单继承,不支持多继承!
    在别的语言中,可能支持多继承,class 子类名 extends 父类名1,父类名2,…{}
  2. 在Java中,类与类之间虽然不能 多继承,但是可以 多层继承!
//定义两个父类
class GrandFather{
	public void function(){
		System.out.println("我是爷爷...") ;
	}

}
class Father extends GrandFather{
	
	public void method(){
		System.out.println("我是老子...") ;
	}
}
/*
class Month{

}
*/

//子类
//class Son extends Father,Monther{} 多继承:Java语言中:类与类之间不支持
//正常的写法:单继承
class Son extends Father{
	public void show(){
		System.out.println("我是仔仔...") ;
	}
}

//测试类
class ExtendsDemo{
	public static void main(String[] args){
		
		//创建子类对象
		Son son = new Son() ;
		son.show() ;
		son.method() ;
		son.function() ;		
	}
}

继承中,一个类的组成
1)成员变量
2)构造方法
3)成员方法

在继承中,成员变量访问:遵循一个原则: "就近原则"

  1. 子类继承父类,如果子类的成员变量名称和父类的成员变量名称不一致的情况: 比较简单,分别访问即可
  2. 子类继承父类,如果子类的成员变量名称和父类的成员变量名称一致的情况:
    a)现在本类的局部位置找,有没有这个变量,如果有就直接使用
    b)如果本类的局部位置没有,在本类的成员位置中找,如果有,就使用
    c)如果本类的成员位置没有,在它的父类的成员位置找,如果有,就使用
    d)父类的成员位置都没有这个变量,压根不存在,就报错!
//父类
class Fu{
	
	int num = 100 ;//父类的成员变量
	
}
//子类
class Zi extends Fu{
	//int num2 = 200 ;
	int num = 200 ; //子类的成员变量
	//成员方法
	public void show(){
		int num = 50 ;  //就近原则
		System.out.println(num) ;
	}
}


//测试类
class ExtendsDemo2{
	public static void main(String[] args){
		
		//创建子类对象
		Zi zi = new Zi() ;
		//System.out.println(zi.num) ;
		//System.out.println(zi.num2) ;
		zi.show() ;	
	}
}

输出: 50

继承关系中,构造方法的访问

子类继承父类,不能够继承构造方法,但是可以通过super()间接访问父类的构造方法,让父类先进行初始化

子类的所有构造方法都默认访问父类的无参构造方法:子类的每一个构造方法的第一句话:super();

super:代表的是否父类的空间标识(代表父类的对象的地址值引用)

为什么要去让父类初始化呢?
因为子类继承父类,可能会使用到父类的数据!所有必须先让父类初始化,然后子类在初始化(分层初始化)

在写父类的时候:在继承中由于存在默认的访问问题,建议永远给出父类的无参构造方法

为什么必须在第一行?
super();必须让非父类先初始化,防止这个类被初始化多次(构造方法)

//父类
class Fu{
	
	public  Fu(){
		System.out.println("这是父类的无参构造方法") ;
	}
	
	
	public Fu(String name){
		System.out.println("这是父类的有参构造方法") ;
	}
	
	
}

//子类
class Zi extends Fu{
	
	public Zi(){
		//隐藏,可以不写
		//super() ;
		System.out.println("这是子类的无参构造方法...") ;
		//super() ;  //要显示 super,必须在第一句话
	}
	public Zi(String name){
		//super() ;
		System.out.println("这是子类的有参构造方法") ;
	}
} 


//测试类
class ExtendsDemo3{
	public static void main(String[] args){
		
		//创建子类对象
		Zi zi = new Zi() ; //无参构造方法
		System.out.println("------------------") ;
		Zi zi2 = new Zi("hello") ; //子类有参构造方法
	}
}

问题:
子类继承父类,那么如果父类的无参构造方法没有,子类的构造方法会出现什么问题,以及如果存在问题,如何解决?

子类的所有构造方法都会报错:存在默认访问:子类的所有构造方法都默认访问父类的无参(第一句:super())

解决办法:

  1. 手动给出父类的无参构造方法
  2. 在子类的所有构造方法中,间接通过super(xx):访问父类的有参构造方法
  3. 子类的所有构造方法的一个只要能够让父类初始化即可!(不推荐!)
    执行子类无参构造方法,先执行本的有参构造方法:this(xx)
    然后在通过本类的有参构造方法 :super(xx):间接访问父类的有参构造,完成父类初始化
//父类
class Fu{	
	/*
	public Fu(){
	
	}
	*/

	public Fu(String name){
		System.out.println("这是父类的有参构造方法") ;
	}
}
//子类
class Zi extends Fu{	
	public Zi(){
		//super("随便给") ; //访问父类的有参构造
		this("hello") ; //本类的有参构造方法
		System.out.println("这是子类的无参构造方法...") ;
	}
	public Zi(String name){
		super("随便给") ;//访问父类的有参构造
		System.out.println("这是子类的有参构造方法") ;
	}
} 
//测试类
class ExtendsDemo4{
	public static void main(String[] args){
			Zi zi = new Zi() ;
			System.out.println("-------------------") ;
			Zi zi2 = new Zi("javaee") ;
	}
}

this和super的区别
this:代表的本类对象的地址值引用
super:代表的父类空间标识(父类对象的地址值引用)

this.变量:访问的本类的成员变量
this.方法名():访问的本类的成员方法
this()/this(xx):访问本类的无参构造方法/访问的本类的有参构造方法

super.变量名:访问的是父类的成员变量
super.方法名():访问的父类的成员方法
super()/super(xx):访问的父类的无参构造方法/访问的父类的有参构造方法

需求:最终输出的结果:30,20,10
考点:继承关系中,成员变量访问问题 :如果子类继承父类,成员变量名称都一致, 就近原则
需要现在子类的局部位置找,有就使用
没有,在子类的成员位置找,有就使用
没有,在父类的成员位置找,有就使用

//父类
class Fu{
	int num = 10 ;
}
//子类
class Zi extends Fu{
	int num = 20 ;
	public void show(){
		int num = 30 ;
		//补全代码
		System.out.println(num) ;   	//输出30
		System.out.println(this.num) ;	//输出20
		System.out.println(super.num) ;	//输出10
	}
}
class Test{

	public static void main(String[] args){
		Zi zi = new Zi() ;
		zi.show() ;
	}
}

猫狗案例---->继承版
猫存在属性:姓名,年龄,颜色;行为:吃,睡,玩游戏
狗存在属性:姓名,年龄,颜色;行为:吃,睡,看门

将两个事物的共性内容抽取在一个独立的类中:
动物事物
属性:姓名,年龄,颜色 属性私有化
行为:吃,睡
公共访问方法:setXXX()/getXXX()

猫事物 和狗事物分别继承自动物,
在每一个具体的事物中:提供对应的构造方法…
定义动物类,猫类和狗类,分别测试

//定义一个动物类
class Animal{
	//姓名,年龄,颜色   属性私有化
	private String name ;
	private int age ;
	private String animalColor ;
	
	//父类的无参构造方法
	public Animal(){
		
	}
	//有参构造方法
	public Animal(String name,int age,String animalColor){
		this.name = name ;
		this.age = age ;
		this.animalColor = animalColor ;
	}
	
	//公共访问方法
	public void setName(String name){
		this.name = name ;
	}
	public String getName(){
		return name ;
	}
	
	public void setAge(int age){
		this.age = age ;
	}
	
	public int getAge(){
		return age ;
	}
	public void setAnimalColor(String animalColor){
		this.animalColor = animalColor ;
	}
	
	public String getAnimalColor(){
		return animalColor ;
	}
	
	//其他成员方法:吃,睡
	public void eat(){
		System.out.println ("动物饿了就需要吃饭...") ;
	}
	
	public void sleep(){
		System.out.println ("动物困了都需要休息...") ;
	}
	
}

//猫类 继承自动物类
class Cat extends Animal{
	
	//无参构造方法
	public Cat(){
		//super() ; //省略
	}
	
	//子类的有参构造
	public Cat(String name,int age,String animalColor){ //"tom",5,"蓝色"
		
		super(name,age,animalColor) ; //访问父类的有参构造方法
	}
	
	
	//子类的特有功能
	public void playGame(){
		System.out.println("猫完毛线...") ;
	}
	
}

//狗类
class Dog extends Animal{
	//无参构造方法
	public Dog(){
		//super() ; //省略
	}
	
	//子类的有参构造
	public Dog(String name,int age,String animalColor){ //"小白",3,"白色"
		
		super(name,age,animalColor) ; //访问父类的有参构造方法
	}
	
	//特有功能
	public void lookDoor(){
		System.out.println("狗可以看家...") ;
	}
}
//测试类
class ExtendsTest{
	public static void main(String[] args){
		
		//测试狗类
		//方式1:无参构造+setXXx()
		Dog d = new Dog() ;
		d.setName("小白") ;
		d.setAge(3) ;
		d.setAnimalColor("白色") ;
		System.out.println("当前狗的姓名是:"+d.getName()+",年龄是:"+d.getAge()+",颜色是:"+d.getAnimalColor()) ;
		d.eat() ;
		d.sleep() ;
		d.lookDoor() ;
		
		System.out.println("----------------------------") ;
		//方式2:有参构造+getXXX()
		Dog d2 = new Dog("旺财",5,"黑色") ;
			System.out.println("当前狗的姓名是:"+d2.getName()+",年龄是:"+d2.getAge()+",颜色是:"+d2.getAnimalColor()) ;
		
		d2.eat() ;
		d2.sleep() ;
		d2.lookDoor() ;		
		//猫类自己 测试		
	}
}

二. 方法重写

方法重写:在继承中,子类出现了和父类一模一样的方法,子类的方法会将父类的方法覆盖掉! (方法重写/方法复写/方法覆盖…)

方法重载overLoad和方法重写override的区别?

方法重载overLoad:
定义方法的时候,如果多个方法的方法名相同,参数列表不同,与返回值无关
参数列表不同:
参数个数不同
参数类型不同

public boolean compare(int a,int b){}
public boolean compare(float a,float b){}

方法重写override:
子类继承父类,出现了和父类一模一样的方法声明(权限,返回值,方法名,参数列表都相同),方法覆盖
为了将父类的功能覆盖掉,使用自己的功能!

class Phone{
    //打电话
    public void callPhone(){
        System.out.println("手机可以打电话了...");
    }
}
//子类:newPhone
class NewPhone extends  Phone{

    //子类和父类出现一模一样的方法:方法名相同,返回值相同,权限修饰符都一样
    //@Override :jdk提供的内置注解 ,可以不写
    public void callPhone(){
        super.callPhone();//访问父类的成员方法
        System.out.println("手机可以看天气预报了") ;
    }

    public void show(){}
}
//测试类
public class ExtendsDemo {
    public static void main(String[] args) {
        //创建手机类对象
       // Phone p = new Phone() ;
        //p.callPhone();

        //优化之后:测试
        NewPhone newPhone =  new NewPhone() ;
        newPhone.callPhone();
    }
}

北方人:属性:姓名,年龄,性别;行为:爱吃面,爱考公
人:属性:姓名,年龄,性别;行为:睡
北方人和南方人都继承Person

//人类
public class Person {
    //姓名
    private String name ;
    //年龄
    private int age ;
    //性别
    private String gender ;

    //alt+ins--->Constructor --->select none
    //无参构造方法
    public Person() {
    }

    //有参构造
    public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    //setXXX(xx)/getXXX()
    //alt+ins---->gettter and setter---->属性全部选中---就会自动生成
    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 String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    //睡觉
    public void sleep(){
        //sout--->System.out.println();
        System.out.println("人困了就需要休息...");
    }
}
//北方人
public class NorthPerson extends  Person {

    public NorthPerson() {
    }

    public NorthPerson(String name, int age, String gender) {
        super(name, age, gender);
    }

    public void eat(){
        System.out.println("北方人爱吃面") ;

    }
    public void study(){
        System.out.println("北方人爱钻研") ;
    }
}
//南方人
public class SourthPerson extends  Person {

    //alt+insrt--->Constructor:将无参/有参全部给出
    public SourthPerson() {
    }

    public SourthPerson(String name, int age, String gender) {
        super(name, age, gender);
    }

    //特有功能
    public  void eat(){
        System.out.println("南方人爱吃米饭");
    }
    public void business(){
        System.out.println("南方人爱经商...");
    }
}
//测试类
public class ExtendsTest {
    public static void  main(String[] args){
        //测试南方人:
        //第一种方式:无参构造+setXXX()赋值

        SourthPerson sp = new SourthPerson();
        //赋值
        sp.setName("高圆圆");
        sp.setAge(41);
        sp.setGender("女");
        System.out.println(sp.getName()+"---"+sp.getAge()+"---"+sp.getGender());
        sp.sleep();
        sp.business();
        sp.eat();

        System.out.println("----------------------");

        //方式2:有参构造直接赋值+getxxx()
        SourthPerson sp2 = new SourthPerson("赵又廷",45,"男");
        System.out.println(sp2.getName()+"---"+sp2.getAge()+"---"+sp2.getGender());
        sp2.sleep();
        sp2.business();
        sp2.eat();
    }
}

三. 继承中成员方法的访问问题

继承中成员方法的访问:

如果子类和父类的成员方法名称不一致,分别访问即可!

子类和父类的成员方法名称一致,

  1. 子类中存在,访问子类的;
  2. 如果不存在,访问父类,如果父类没有,报错!
//定义一个父类
class Fu{

    public void method(){
        System.out.println("method fu...");
    }
}

//子类
class Zi extends  Fu{
    public void show(){
        System.out.println("show zi...");
    }

    public void method(){
        System.out.println("method zi...");
    }
}

//测试类
public class ExtendsDemo {
    public static void main(String[] args) {
        //创建子类对象
        Zi z = new Zi() ;
        z.show();
        z.method();
    }
}

成员方法的分类:是否存在返回值,是否带参

  1. 带参的,没有返回值;
  2. 带参的,有返回值;
  3. 不带参的,没有返回值;
  4. 不带参的,有返回值;

根据具体需求然后定义相对应的功能!
参数类型: 基本类型/引用类型
返回值类型: 基本类型/引用类型
如果子类和父类的成员方法名称不一致,分别访问即可!

子类和父类的成员方法名称一致,
子类中存在,访问子类的;
如果不存在,访问父类,如果父类没有,报错!

//定义一个父类
class Father{
    //带参的,没有返回值类型
    public void show(String str){
        System.out.println(str);
    }
    //带参的,有返回值类型的
    public String method(int num){
        return "helloworld"+num ;
    }
}

//子类
class Son extends  Father{
    //子类的成员方法
    //不带参的,没有返回值的
    public void function(){
        System.out.println("function son...");
    }
    //不带参的,有返回值的
    public String function2(){
        return "JavaEE" ;
    }
}

//测试类
public class ExtendsDemo2 {
    public static void main(String[] args) {

        //创建子类对象
        Son s = new Son() ;
        s.show("helloworld,javaee");
        String str = s.method(100);
        System.out.println(str);

        s.function();
        String str2 = s.function2();
        System.out.println(str2);
    }
}

子类继承的父类的时候,有时候需要将父类的功能覆盖掉,完成的自己的功能!
但是,有的时候不能覆盖
举例:
有一个父类,父类中有一个方法,show().
定义一个子类,子类将父类的show()覆盖掉,直接将父类的功能覆盖了

现在就不想让父类的功能被覆盖掉,Java提供了一个关键字:final: 状态修饰符 (最终的,无法更改的)
可以修饰类,可以修饰变量,可以修饰方法

class Fu{

    public final void show(){
        System.out.println("这是绝密文件,任何人不能更改");
    }
}
class Zi extends  Fu {

    /*
    public void show() {
        System.out.println("这是一堆垃圾...");
    }
    */

}
//测试类
public class FinalDemo {
    public static void main(String[] args) {
        Zi zi = new Zi() ;
        zi.show();
    }
}

四. final关键字

final:状态修饰符,最终的,无法更改的

  1. 可以修饰类,该类不能被继承
  2. 可以修饰变量,此时这个变量是一个常量:自定义常量
    此时这个变量只能被赋值一次,再次赋值,报错!(成员变量被final修饰,必须先初始化)
  3. 可以修饰方法,此时这个方法不能被重写!

final的成员位置:public static final int xxx = 值;传统方式定义常量(编译时期常量)
public final Student s = new Student();实例化(创建对象)
变量(运行时期变量)

class Fu2{

    //成员变量被final:必须先初始化
    public final int num = 100 ;
    public final void show(){
        System.out.println("show fu2...");
    }

}

class Zi2 extends  Fu2{
    int num2 = 20 ;
    /*
    public void show(){
        System.out.println("show zi2...");
    }
     */
}

public class FinalDemo2 {
    public static void main(String[] args) {
        //创建子类对象
        Zi2 zi = new Zi2() ;
        System.out.println(zi.num);
        //zi.num = 200 ;
        zi.show();
    }
}

final关键字修饰基本类型/引用类型的区别

当final修饰基本数据类型,基本数据类型的值不能再改变,只能赋值一次!

开发中,定义基本数据类型的常量:
在一个类的成员位置:
public static final int xxx = xx ;

当final修饰引用数据类型,引用数据类型的地址值不能再改变!

class Demo{

    //传统定义方式
    public static final int s1 = 100 ;
    public static final int s2 = 200 ;
    public static final int s3 = 300 ;
    public static final int s4 = 400 ;

    public static final String str1 = "FRONT" ;
    public static final String str2 = "BEHIND" ;
    public static final String str3 = "LEFT" ;
    public static final String str4 = "RIGHT" ;

    //常量:引用类型
    public static final Student s = new Student() ; //静态实例变量

}
enum  Demo2{ //枚举类型(JDK5以后的枚举)

    //一堆字符串常量
    FRONT,BEHIND,LEFT,RIGHT;

}

//定义学生类
class Student{
     int age = 20 ; //成员位置
}


//测试类
public class FinalDemo {
    public static void main(String[] args) {

        //定义一个变量:
        int num = 100 ;
        System.out.println(num);//这是一个变量
        System.out.println("--------------------");

        //定义final修饰的变量
        final int num2 ;//final修饰的基本数据类型的变量
        num2 = 200 ; //只能赋值一次,常驻内存(已经是常量:自定义常量)
        System.out.println(num2);
       // num2 = 300 ;

        System.out.println("---------------------");
        int x = 100 ;
        x = 1000 ;
        System.out.println(Demo.s1) ;
        System.out.println(Demo.s2) ;
        System.out.println(Demo.s3) ;
        System.out.println(Demo.s4) ;

        System.out.println("------------------------");

        //创建一个学生类对象;
        //改变当前类中的成员变量:依然可以更改,但是此时不能在重新new对象了
       final Student s = new Student() ;//com.qf.extends_01.Student@1540e19d
        System.out.println(s);
        s.age = 30 ;
        System.out.println(s.age);
        s.age  = 25 ;
        System.out.println(s.age);

       // s = new Student() ; 不能重新进行实例化了(final修饰之后,学生对象的地址值无法改变的!)
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值