day11【final、权限、内部类】

China☆狼群 提交于 2020-03-08 22:30:15

day11【final、权限、内部类】

第一章 final关键字

1.1 概述

final关键字代表最终、不可改变的内容。

  • final:不可改变。用于修饰类、方法和变量
    • 类:被修饰的类,不能被继承。
    • 方法:被修饰的方法,不能被重写。
    • 变量:被修饰的变量,不能被重新赋值

1.2 使用方法

1.修饰类

含义:当前这个类不能有任何的子类。(太监类)

格式:

public final class 类名称{
    // ......
}

注意:一个类如果是final的,那么其中的所有成员方法都无法进行覆盖重写(因为没有子类)

2.修饰成员方法

当final关键字用来修饰一个方法的时候,这个方法就是最终方法,不能被覆盖重写

格式:

修饰符 final 返回值类型 方法名称(参数列表){
     // 方法体
}

注意事项:

对于类,方法来说,abstract和final关键字不能同时用,因为矛盾

public void Fu{
    public final void method(){
        System.out.println("父类方法执行~");
    }
}

public void Zi extends Fu{
     // 会发生报错
    @Override
    public void method(){
        System.out.println("重写父类方法~")
    }
}

3.修饰局部变量

对于基本数据类型来说:

不可变说的是变量中的【数据】不可变

一次赋值,终身不变

public static void main(String[] args){
    final int num = 10;
     System.out.println(num);// 10
    // num = 20; 报错
}

对于引用类型来说是【地址值】不可变

// 定一个学生类
public class Student{
    private String name;
    public Student(){
        
    }
    public Student(String name){
        this.name = name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

public class Final{
    public static void main(String[] args){
        final Stundet stu = new Student("雷神");
         System.out.println(stu); // 雷神
        //错误写法,final的引用类型变量的地址值不可改变
        // stu = new Student("蛇女");
        // 但是方法可以调用
        stu.setName("金刚狼");
        System.out.println(stu);// 金刚狼
    }
}

4.修饰成员变量

使用final关键字修饰后,那么这个变量是不可能改变的。

成员变量初始化的方法有两种,只能二选一:

  • 显示初始化:

    public class User{
        final String USERNAME = "张三";
        private int age;
    }
  • 构造方法初始化

    public class User{
        final String USERNAME;
        private int age;
        public User(String username,int age){
            this.USERNAME = username;
            this.age = age;
        }
    }

    注意:

    ​ 被final修饰的常量名称,一般都有书写规范,所有字母都大写。

    举例:

    public class Person{
    
        private final String name/* = 雷神*/;
        public Person(){
            name = "蜘蛛侠";
        }
        public Person(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }
    
       // public void setName(String name){
          //  this.name = name;
        //}
    }

第二章 权限修饰符

2.1 概述

在java中提供了四种访问权限,使用不同的访问权限时,被修饰的内容会有不同的访问权限

  • public:公共的
  • protected:受保护的
  • default:默认的(什么都不写)
  • private:私有的

2.2 不同权限的访问能力

public protected default(空的) private
同一类中【我自己】
同一包中(子类无关的类)【我邻居】
不同包的子类【我儿子】
不同包的无关类【陌生人】

所以 public > protected > (defoult) > private

建议:

  • 成员变量使用private,隐藏细节。
  • 构造方法使用public,方便创建对象。
  • 成员方法使用public,方便调用。

不加权限的修饰符,就是(default)。

  1. 外部类:public/(default)
  2. 成员内部类:pubic/protected / (default) / private
  3. 局部内部类:什么都不写。

第三章 内部类

3.1 概述

如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。

例如:身体和心脏的关系 汽车和发动机的关系

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

成员内部类

格式:定义在类中方法外的类。

定义格式:

修饰符 class 外部类名称{
    修饰符 class 内部类名称{
        // ...
    }
    // ...
}

注意:内用外,随意访问,外用内,需要内部类对象。

使用内部类格式

  1. 间接使用:在外部类的方法中,使用内部类,然后main方法调用外部类的方法。

  2. 直接方法

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

代码如下:

public class Body{// 外部类
    
    public class Heart{// 成员内部类
        
        // 内部类方法
        public void beat(){
        System.out.println("心脏跳动");
        System.out.println("我叫:"+name);
        }
    }
    
    //  外部类的成员变量
    private String name;
    
    // 外部类的方法
    public void methodBody{
         System.out.println("外部类的方法");
        new Heart().beat();
    }
    
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name
    }
}

测试类:

public static void main(String[] args){
    // 外部类的对象
    Body body = new Body();
    // 通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
    body.methodBody();
    
    // 按公式写
    Body.Heart heart = new Body().new Heart();
    heart.beat();
    
}

内部类的同名变量访问

访问格式:外部类名称.this.外部类成员变量名

代码如下:

public class Outer {

    // 外部类的成员变量
    int num = 10;
    public class Inner{
        // 内部类的成员变量
        int num = 20;

        public void methodInter(){
            // 内部类的局部变量
            int num  = 30;
            System.out.println(num);// 局部变量  30
            System.out.println(this.num);// 内部变量 20
            System.out.println(Outer.this.num);// 外部类的成员变量 10


        }
    }
}

测试类:

public class Demo02InnerClass {
    public static void main(String[] args) {
        Outer.Inner outer = new Outer().new Inner();
        outer.methodInter();
    }
}
//结果 30  20  10

局部内部类

格式:

修饰符 class 外部类名称{
    修饰符 返回值类型 外部类方法名称(参数列表){
        class 局部内部类{
            // 。。。。。
        }
    }
}

局部内部类final的问题

局部内部类,如果希望访问的局部变量,那么这个局部变量必须是【有效的final的】。

备注:从java 8+开始,只要局部变量事实不变,那么final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量跟着的是方法,在栈内存中。
  3. 方向运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

3.2 匿名内部类【重点】

前提

匿名内部类必须继承一个父类或者实现一个接口

如果接口的实现类(或者父类的子类)只需要使用唯一的一次。

使用条件

那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】

格式

接口名称 对象名 = new 接口名称(){
    //覆盖从写所有抽象方法
};
对象名.方法名称调用
// 或
new 接口名称(){
    //覆盖从写所有抽象方法
}.方法名称调用;

格式解析:

  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现哪个接口
  3. (...)这是匿名内部类的内容

注意问题:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。

    如果希望多次创建对象,而且类的内容一样,就没有必要用匿名内部类了

  2. 匿名对象,在【调用方法】的时候,只能用唯一的一次。

    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。

  3. 匿名内部类是省略了【实现类 / 子类名称】,但是匿名对象省略了【对象名称】

    强调:匿名内部类和匿名对象不是一回事。

代码如下

接口:

public interface Face{
   public abstract void method();
}

创键匿名内部类,并调用

public class Main{
    public void main(String[] args){
         // 格式一
    Face obj = new Face(){
        @Override
        public void method(){
            System.out.println("匿名内部类实现了方法~");
             }
         };
        obj.method();
        
        //  格式二
        new Face(){
        @Override
        public void method(){
            System.out.println("匿名内部类实现了方法~");
             }
         }.method();
        // 因为匿名对象无法调用第二次方法,如果有两个匿名内部类的话,需要在创建一个匿名内部类的匿名对象
        new Face(){
        @Override
        public void methodA(){
            System.out.println("匿名内部类A实现了方法~");
             }
         }.method();new Face(){
        @Override
        public void methodB(){
            System.out.println("匿名内部类B实现了方法~");
             }
         }.methodB();
    }
}

第四章 引用类型用法

4.1 class作为成员变量

定义游戏中的英雄类

class Role{
    // 角色ID
    int id;
    // 生命值
    int blood;
    // 角色名称
    String name;
}

使用 int 类型表示 角色id和生命值,使用 String 类型表示姓名。此时, String 本身就是引用类型,由于使用

的方式类似常量,所以往往忽略了它是引用类型的存在。如果我们继续丰富这个类的定义,给 Role 增加武器,穿 戴装备等属性,我们将如何编写呢?

定义武器类,将增加攻击能力:

class Weapon { 
    String name; // 武器名称 
        int hurt; // 伤害值 
}

定义穿戴盔甲类,将增加防御能力,也就是提升生命值:

class Armour {
    String name;// 装备名称
        int protect;// 防御值 
}

定义角色类:

class Role { 
    int id; 
        int blood;
        String name; // 添加武器属性
        Weapon wp; // 添加盔甲属性
        Armour ar; 
        // 提供get/set方法
        public Weapon getWp() {
        return wp; 
    }
    public void setWeapon(Weapon wp) { 
        this.wp = wp;
    }
    public Armour getArmour() {
        return ar; 
    }
    public void setArmour(Armour ar) { 
        this.ar = ar; 
    }
    // 攻击方法
    public void attack(){
        System.out.println("使用"+ wp.getName() +", 造成"+wp.getHurt()+"点伤害"); }
    // 穿戴盔甲
    public void wear(){ 
        // 增加防御,就是增加blood值
        this.blood += ar.getProtect(); 
        System.out.println("穿上"+ar.getName()+", 生命值增加"+ar.getProtect());
    }
}

测试类:

public class Test {
    public static void main(String[] args) { 
        // 创建Weapon 对象
        Weapon wp = new Weapon("屠龙刀" , 999999); 
        // 创建Armour 对象
        Armour ar = new Armour("麒麟甲",10000);
        // 创建Role 对象
        Role r = new Role();
        // 设置武器属性
        r.setWeapon(wp); 
        // 设置盔甲属性
        r.setArmour(ar); 
        // 攻击
        r.attack(); 
        // 穿戴盔甲 
        r.wear();
    } 
}
输出结果: 
使用屠龙刀,造成999999点伤害
穿上麒麟甲 ,生命值增加10000

类作为成员变量时,对它进行赋值的操作,实际上,是赋给它该类的一个对象。

4.2 interface作为成员变量

接口是对方法的封装,对应游戏当中,可以看作是扩展游戏角色的技能。所以,如果想扩展更强大技能,我们在 Role 中,可以增加接口作为成员变量,来设置不同的技能。

定义接口:

// 法术攻击
public interface FaShuSkill {
    public abstract void faShuAttack(); }

定义角色类:

public class Role {
    FaShuSkill fs;
    public void setFaShuSkill(FaShuSkill fs) {
        this.fs = fs; 
    }
    // 法术攻击 
    public void faShuSkillAttack(){ 
        System.out.print("发动法术攻击:");
        fs.faShuAttack(); 
        System.out.println("攻击完毕");
    } 
}

定义测试类:

public class Test {
    public static void main(String[] args) {
        // 创建游戏角色
        Role role = new Role();
        // 设置角色法术技能 
        role.setFaShuSkill(new FaShuSkill() {
            @Override
            public void faShuAttack() {
                System.out.println("纵横天下");
            }
        }); 
        // 发动法术攻击 
        role.faShuSkillAttack();
        // 更换技能 
        role.setFaShuSkill(new FaShuSkill() {
            @Override 
            public void faShuAttack() {
                System.out.println("逆转乾坤"); 
            }
        });
        // 发动法术攻击 
        role.faShuSkillAttack();
    } 
}
输出结果: 
发动法术攻击:纵横天下
攻击完毕 
发动法术攻击:逆转乾坤
攻击完毕

我们使用一个接口,作为成员变量,以便随时更换技能,这样的设计更为灵活,增强了程序的扩展性。

接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象

4.3 interface作为方法参数和返回值类型

public class DemoInterface {

    public static void main(String[] args) {
        // 左边接口名称,右边实现类名称   多态写法
        List<String> list = new ArrayList<>();

        List<String> result = addName(list);
        for (int i = 0; i < result.size(); i++) {
            System.out.println(result.get(i));
        }
    }
    public static List<String> addName(List<String> list){
        list.add("雷神");
        list.add("火麒麟");
        return list;
    }
}

接口作为参数时,传递它的子类对象。

接口作为返回值类型时,返回它的子类对象

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!