zoukankan      html  css  js  c++  java
  • 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;
        }
    }
    
    

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

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

  • 相关阅读:
    code#5 P2 棋子
    code#5 P1 报告
    ztz11的noip模拟赛T3:评分系统
    20181101noip模拟赛T1
    20181031noip模拟赛T2
    20181031noip模拟赛T1
    Linux进程的五个段
    进程和线程有什么区别
    shell中if条件字符串、数字比对,[[ ]]和[ ]区别
    Python实现单例模式
  • 原文地址:https://www.cnblogs.com/anke-z/p/12444754.html
Copyright © 2011-2022 走看看