java自学第3期——继承、多态、接口、抽象类、final关键字、权限修饰符、内部类


一.继承:

关键字extends

/*
定义一个父类:人类
定义父类格式:public class 父类名称{
}
定义子类格式:public class 子类名称 extends 父类名称{
}
*/
  • 代码示范:
    *在这里插入图片描述要点:
    1.子类中在main方法中创建父类对象可调用父类方法;
    2.不加关键字直接访问本方法局部变量(可重名时区分);
    3.使用this关键字访问本类成员变量(可重名时区分);
    4.使用super关键字访问父类成员变量(可重名时区分);
    5.如果存在方法的重名:父类与子类有重名方法:
    ——对象new的谁则优先调用谁的重名方法,没有则向上寻找;
    前面介绍了成员变量、成员方法继承的访问特点,接下来是构造方法:
    ——子类构造方法(父类无参时)中有一个默认赠送的super()方法
    ——父类有参时,子类里调用super的()里传参,重载时谁对的上调用谁;
    6.如果要更新修改父类方法,本着设计原则尽量不去直接修改正在使用的类,
    则可以进行覆盖重写:格式:通过使用super关键字继承父类需要的方法。
@Override
方法外部相同(){
   super.父类方法();
   //这里添加新的内容
}
/*
小结super
super关键字作用:
示例:
   public class demo06Super extends demo01people {

   public void method1(){
       System.out.println(super.num);	//1.在子类成员方法中访问父类的成员变量
   }
   public void method2(){
       super.methodChongMing();	// 2.在子类成员方法中访问父类的成员方法
   }
   public demo06Super(){
       super();	//3.在子类的构造方法中访问父类的构造方法
   }
}
*/

7.this关键字的作用小结:

this关键字的作用:
1.在本类的成员方法中访问本类的成员变量
2.在本类成员方法中访问本类的另一个成员方法
3.在本类的构造方法中访问本类的另一个构造方法:
(1)this(...)调用必须是构造方法的第一个语句,一个this;
(2)super和this两种构造调用不能同时使用
  • 代码示范:
String name = "python";
public void method1(){
        String name = "python";
        System.out.println(name);//无关键字时直接访问本方法局部变量
        System.out.println(this.name);//访问本类成员变量
        System.out.println(super.num);//访问父类成员变量
        this.method2();//访问本类另一个成员方法
    }

8.最后,继承三大特点:

1.单继承:一个子类只有一个直接父类
2.多级继承:父类、子类、子类也可作父类再向下延伸,最上级为java.lang.object类
3.一个父类可以有多个子类

二、多态

1.多态性:父类引用指向子类对象
多态的一个用处:无论右边new的时候换成哪个子类对象,等号左边调用方法都不会发生变化

格式:
父类名称 对象名 = new 子类名称();
或者:
接口名 对象名 = new 实现类名称()。

2.成员方法:编译看左,运行看右;
成员变量:编译看左,运行也看左;实例变量不具备多态性.
代码示范:

//前面省略部分Zi类已经继承Fu类
  public static void main(String[] args) {

        Fu one = new Zi();//多态的写法
        one.methodcommon();//重名时,成员方法优先使用子类(new谁先调用谁)。

        one.fu1();//它的编译时类型为Fu,可以调用Fu中的方法
//      one.zi1(); 该调用编译时会报错,因为它的编译时类型为Fu,无法调用zi1方法;
        System.out.println(one.num);//优先看左边Fu类

        Zi two = (Zi) one;//将父类对象还原成子类对象。
        System.out.println(one instanceof Fu);//输出true,one可以做Fu类的实例对象
        System.out.println(one instanceof Zi);//输出true,one可以做Zi类的实例对象
    }

3.转型多态写法左父右子是正常的向上转型
4.向下转型:为了让对象调用子类方法(向上转型只能调用左边编译类型的父类方法)
左子类右父类,是一个还原的过程,将父类对象还原成子类对象,且不能向下转成别的子类。

格式:  子类名 对象名 = (子类名) 父类对象名;     (后者也可以是接口)

Fu fu = new Fu();//向上转型创建父类对象
Zi two = (Zi) one;//将父类对象还原成子类对象。

5.强制向下转型时,判断前面的对象是否是后面类型的实例,是否可以成功转换,从而保证代码更加健壮。
格式: 对象 instanceof 类型
得到一个boolean值结果(true/false),判断前面的对象能否作为后面类型的实例

Zi two = (Zi) one;//将父类对象还原成子类对象。
        System.out.println(one instanceof Fu);//输出true,one可以做Fu类的实例对象
        System.out.println(one instanceof Zi);//输出true,one可以做Zi类的实例对象

三、接口

1.接口定义了某一批类需要遵守的规范。这就意味着接口里通常是定义一组公用方法。
2.接口是一种引用数据类型(类、接口、数组),注意其中的抽象方法。
3.接口可以继承接口,但不能继承类。
4.Java9 里可以有常量、抽象方法、默认方法、静态方法、私有方法。
5.备注:接口编译后生成的同样是.class的字节码文件

//格式
public interface 接口名称(首字母大写){
	//抽象方法
}
  • 接口里的成员变量只能是常量,必须用public static final修饰,可省略
 //final即为不可改变
    public static final int MAX_NUMBER = 20;
  • 接口里的[普通方法]只能是抽象方法,public abstract可以省略
 public abstract void out();
   				 void getDate();
  • 接口中的[默认方法]需要用default修饰
    /*
    当接口新添加方法时,新方法写为默认方法,则可以不去动其实现类,默认方法自动被其继承
    默认方法同样可以被覆写。
     */
    public default void print() {
        foo();
        System.out.println("默认方法调用3");
    }
  • 接口中定义[静态方法],需要用static修饰
public static void staticTest() {
        System.out.println("静态方法!");
    }
  • 当俩默认方法中有重复内容时,抽取出来定义私有方法
//定义私有默认方法,给默认方法调用,但不应被实现类使用,所以权限为私有
    private void foo() {
        System.out.println("默认方法调用2");
    }
    
//定义私有静态方法,给静态方法调用,但不应被实现类使用,所以权限为私有
    private static void bar() {
        System.out.println("bar私有静态方法");
    }
}
  • 接口不能创建实例,但能用于声明引用类型变量,且必须引用到实现类的对象;
  • 一个实现类可以同时实现多个接口
//格式:
.public class 实现类名称 impliments{
 	//必须覆写接口中所有抽象方法;
},	
  • 在实现类(impliments)中进行接口的实现:
//
public class Demo01InterfaceImpl implements Demo01Interface,Demo02Interface {
    //1,2接口都有out抽象方法,但只需覆写一次
    @Override
    public void out() {
        System.out.println("抽象方法覆写!");
    }

    @Override
    public void getDate() {
        System.out.println("抽象方法覆写!");
    }

    @Override
    public void print(){
        System.out.println("冲突的默认方法也需要覆写!");//不冲突则不用覆写
    }
}
  • 然后在main类里创建实现类的对象,进行调用
public class Impliments  {

    public static void main(String[] args) {
  	    //创建实现类的对象
        Demo01InterfaceImpl ImplementationObject1 = new Demo01InterfaceImpl();
        ImplementationObject1.getDate();
        ImplementationObject1.out();
        ImplementationObject1.print();//调用实现类里继承自接口的默认方法。
        
		//接口里的静态方法只能通过接口名称直接调用。
        Demo01Interface.staticTest();

		//通过接口名直接访问常量。
        System.out.println(Demo01Interface.MAX_NUMBER);
    }
}
  • 接口是多继承,一个接口可以有多个父接口。
  • 多个父接口中的抽象方法如果存在重名,正常覆写;但默认方法重名需要带有default关键字覆写。
  • 正常不冲突抽象方法不需要再覆写(实现类多个接口时也是如此)。
//接口的多继承,同样使用extends关键字
public interface Demo03InterfaceExtends extends Demo01Interface,Demo02Interface {
    @Override
    default void print() {
		//覆写父接口的默认方法不能省略default关键字
    }
}

四、抽象类

  • 抽象方法:加上abstract关键字,去掉大括号,直接分号结束;
  • 抽象类:抽象方法所在的类必须是抽象类,再class之前写上abstract即可
  • 不能直接new抽象类对象,必须用一个子类来继承抽象父类
    (抽象类对象调用的方法存在没有具体内容的方法体,因此没有意义)
  • 创建子类对象,不可创建抽象父类对象;
public abstract class demo01Abstract {

    public abstract void method1();
    public abstract void method2();
}
  • 子类必须覆写抽象类中的所有抽象方法(去掉abstrat,补上大括号)。
    (假设不全部覆写,对象调用的方法存在抽象方法,没有意义)
//使用extends继承抽象父类
public abstract class demo02Zi extends demo01Abstract {

    //只覆写了一个抽象方法,没有将抽象方法全部覆写完,也就是说本子类还存在着继承下来的未覆写的抽象方法
    //所以该类也同样是抽象类
    
    @Override
    public void method1() {
        System.out.println("已经覆写第method1方法!");
    }
}
//在这个子子类中已经将所有抽象方法全部覆写,所以该类不再是抽象类!
public class demo03Sun extends demo02Zi {
    @Override
    public void method2() {
        System.out.println("已经覆写method2方法!");
    }
}
  • 最后在main类中的main方法里创建对象进行调用即可。

五、final关键字

  • final表示它修饰的类、方法、成员变量、局部变量不可改变。
  • final修饰的类不可被继承下去。无子类,不可被覆盖重写。
public final class 类名(){}
  • final修饰的方法不可被覆盖重写。
public final void method(){}
  • 类和方法不能同时使用final和abstract,二者矛盾。abstract表示抽象,是待定的;final表示最终的,是确定的。
  • final修饰局部变量,若是基本数据类型的数值,则不可改变;若是引用数据类型,则地址值不可改变,但地址指向的对象的内容可以改变。
  • final修饰成员变量,也不可变,但必须要进行手动赋值,成员变量加final后不允许再拥有默认值。
public final class Demo01Final {

    public static void main(String[] args) {
        int NUM = 1;
        System.out.println(NUM);
        NUM = 2;//可以改变
        System.out.println(NUM);
        
        final int NUM1 = 2;
//      NUM1 = 3;  错误,NUM是确定的值,不可被改变。
    }
}

六、权限修饰符

  • 权限大小:
    四种权限修饰符,访问权限从大到小:
    public > protected > 空 > private
    同一个类: yes yes yes yes
    同一个包: yes yes yes no
    不同包子类: yes yes no no
    不同包非子类:yes no no no
    在这里插入图片描述

七、内部类

一个外部类包含的一个嵌套的类,叫内部类。
分类:
1.成员内部类。
2.局部内部类(包含匿名内部类)。

  • 注意:内部类可以无限制地访问外部类,外部类访问内部类需要内部类对象。
  • 各个类对应可以使用的权限修饰符如下:
    外部类:public/(default)
    成员内部类:public.protect.(default).private
    局部内部类:什么都没有,注意并不是(default)
  • 内部类定义示例:
public class main {
    private String name = "成员变量";

    //定义一个成员内部类
    public class innerClass {
        private String innername = "内部成员变量";
    }

		// 定义一个成员内部类内的成员方法
        public void innermethod1() {
            System.out.println("内部类方法");
            System.out.println(name);
        }
        
  }

使用内部类的两种方式:
1.直接:在main方法中创建内部类对象:格式:
外.内 = new 外().内();

外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

 public static void main(String[] args) {
        //直接创建内部类的对象。
        InnerclassUsing.innerClass object = new InnerclassUsing().new innerClass();
        object.innermethod1();
    }

2.间接:在外部类的方法中,只用内部类,main只是调用外部类的方法,通过外部类方法访问内部类。

public class InnerclassUsing {

    private String name = "成员变量";

    //定义一个成员内部类
    public class innerClass {
    
        private String innername = "内部成员变量";

        // 定义一个成员内部类的成员方法
        public void innermethod1() {
            System.out.println("内部类方法");
            System.out.println(name);
        }
    }

    public void outMethod() {
        System.out.println("外部类成员方法");
        
  //    System.out.println(innername);  错误。
  
        //通过匿名对象访问内部类变量和方法
        System.out.println(new innerClass().innername);
        
        //创建匿名对象并调用innermethod1方法。
        new innerClass().innermethod1();
    }
    
 }

内部类的重名变量访问格式:

  • 本方法: 空
  • 本类成员变量:this.
  • 外部类成员变量:外部类的名称.this.
public class Demo03CommonName {
	//外部类成员变量
    int NAME = 1;

    public class innerClass{
    	//内部类成员变量
        int NAME = 2;
        
        public void method (){
        	//内部类局部变量
            int NAME = 3;
            
            System.out.println(NAME);//3,局部变量,就近原则。
            System.out.println(this.NAME);//2,本类成员变量。
            System.out.println(Demo03CommonName.this.NAME);//1,外部类的成员变量;
        }
    }
}

如果一个类定义在方法内部,那么这个类叫局部内部类。
局部内部类只能被当前所属的方法所使用,外部不可。

格式:

public class Localinnerclass {
    String NAME = "外部成员变量";

    public void method(){
    
    	//定义局部内部类
        class localLinnerClass{
            String ONE = "局部内部类成员变量";
            
            public void nmethod2(){
                System.out.println("局部内部类的成员方法");
                System.out.println(ONE);
            }
        }

    }
}

局部内部类,如果希望访问所在方法的局部变量,该变量应该满足【有效final】的条件

public class Demo04Final {

    public void method1(){
    
        //这里即使不写final的话,不去修改 也算是有效final [从java8开始]
        final String NAME = "bilibili";

        class LocalInnerclass{
        
            public void localInnerMethod(){
            	//局部内部类内的方法访问所在类外部的方法的局部变量
                System.out.println(NAME);
            }
        }

    }
}

当接口是实现类只使用唯一一次时,可以使用匿名内部类

  • 格式
接口名称 对象名 = new 接口名称(){
    //这里进行抽象方法的覆写。
};    //别忘了结尾的分号

匿名内部类在创建对象的时候,只能使用唯一一次
匿名对象在使用方法时,只能使用唯一一次
匿名内部类是省略了(实现类/子类名称),而匿名对象则是省略了对象名称,二者不同。

//当实现类只使用一次时,可以使用匿名内部类的写法!
        Anonymous obj6 = new Anonymous() {
            //覆写匿名内部类里所有抽象方法!
            @Override
            public void method1() {
                System.out.println("bilibili?");
            }
            @Override
            public void method2(){
                System.out.println("覆写method!");
            }
        };
        obj6.method1();//打印内容
        obj6.method2();

将类作为成员变量的写法

//定义一个武器类
public class Weapon {
    private String itsname;//武器名称
    private int attacknum;//武器攻击力
		
	//无参构造方法
    public Weapon() {
    }
	//全参方法
    public Weapon(String itsname, int attacknum) {
        this.itsname = itsname;
        this.attacknum = attacknum;
    }
	//get名字
    public String getItsname() {
        return itsname;
    }
	//set名字
    public void setItsname(String itsname) {
        this.itsname = itsname;
    }
	//get攻击力
    public int getAttacknum() {
        return attacknum;
    }
	//set攻击力
    public void setAttacknum(int attacknum) {
        this.attacknum = attacknum;
    }
}

//定义一个女武神类
public class Valkyrie1 {
    private String name;//女武神之名
    private Weapon weapon;//将武器类变为成员变量 交给女武神类

	//女武神攻击方法
    public void attack(){
        System.out.println(name + "使用的" + weapon.getItsname() + "具有" + weapon.getAttacknum() +"点攻击力");
    }

    
	//无参构造
    public Valkyrie1() {};
	//全参
    public Valkyrie1(String name, Weapon weapon) {
        this.name = name;
        this.weapon = weapon;
    }
	//get女武神名字
    public String getName() {
        return name;
    }
	//set女武神名字
    public void setName(String name) {
        this.name = name;
    }
	//get女武神武器
    public Weapon getWeapon() {
        return weapon;
    }
	//set女武神武器
    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}
  • 定义了女武神类和武器类后,在main方法中进行调用
 		Valkyrie1 valkyrie = new Valkyrie1();//创建一个女武神
        valkyrie.setName("bronya"); //set女武神名字叫bronya
        
        Weapon weapon = new Weapon("真理之钥",1000);//创建一把武器,并同时赋予名称和攻击力
//     也可以分开写: weapon.setItsname("真理之钥");//武器名字
//     也可以分开写: weapon.setAttacknum(1000);//武器攻击力

        valkyrie.setWeapon(weapon);//将定义好的武器交给女武神
        
        valkyrie.attack();//最后,女武神使用这把武器进行攻击
posted @ 2020-12-07 18:10  崩天的勾玉  阅读(101)  评论(0编辑  收藏  举报