• Chapter11【final、权限、内部类】


    Chapter11【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;
        }
    }
    
    

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

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

  • 相关阅读:
    021.day21 反射 Class类 反射常用操作
    020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步
    019.day19 缓冲流 对象流 标准输入输出流
    018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码
    017.day17 Map接口 克隆 treeSet集合排重缺陷
    016.day16 HashSet TreeSet 比较器Comparable Comparator
    015.day15
    014.day14
    013.day13
    线程
  • 原文地址:https://www.cnblogs.com/anke-z/p/12444754.html
Copyright © 2020-2023  润新知