一、final关键字

1.1 概述

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承 API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了 final 关键字, 用于修饰不可改变内容

1.2 使用方式

修饰类

  1. final class 类名{
  2. }

查询API发现像 public final class Stringpublic final class Mathpublic final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容

修饰方法

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

重写被final修饰的方法,编译时就会报错

修饰变量

1.局部变量—基本类型

基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。

  1. public class FinalDemo01{
  2. public static void main(String[] args){
  3. //声明变量,使用final修饰
  4. final int a;
  5. //第一次赋值
  6. a=10;
  7. //第二次赋值
  8. a=20;//报错
  9. }
  10. }

2.局部变量—引用类型

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的 修改,代码如下:

  1. public class FinalDemo02{
  2. public static void main(String[] args){
  3. //创建User对象
  4. final User u=new User();
  5. //创建 另一个User对象
  6. u= new User(); //报错,
  7. //调用setName方法
  8. u.setName("张三");//可以修改
  9. }
  10. }

3.成员变量

成员变量涉及到初始化的问题,初始化方式有两种,只能二选一

  • 显示初始化
  1. public class User{
  2. final String USERNAME="张三";
  3. private int age;
  4. }
  • 构造方法初始化
  1. public class User{
  2. final String USERNAME;
  3. private int age;
  4. //全参构造
  5. public User(String username,int age){
  6. this.USERNAME= username;
  7. this.age=age;
  8. }
  9. }

被final修饰的常量名称,书写规范:所有字母大写

二、权限修饰符

2.1 概述

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

  • public: 公共的
  • protected: 受保护的
  • default : 默认的
  • private: 私有的

2.2 不同权限的访问能力

public protected default(空的) private
同一类中
同一包中(子类与无关类)
不同包的子类
不同包中的无关类

public具有最大权限下,private是最小权限

编写代码时,如果没有特殊的考虑,建议这样使用权限:

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

PS:不加权限修饰符,则默认为default

三、内部类

1.1 概述

什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类

1.成员内部类

成员内部类定义在类中方法外的类。

  1. class 外部类{
  2. class 内部类{
  3. }
  4. }

在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类 Car 中包含发动机 类 Engine ,这时, Engine 就可以使用内部类来描述,定义在成员位置。

  1. class Car{//外部类
  2. class Engine{//内部类
  3. }
  4. }

访问特点
  • 内部类可以直接访问外部类的成员,包括私有成员
  • 外部类要访问内部类的成员,必须要建立内部类的对象

创建内部类对象格式:

  1. 外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
  1. public class Person{
  2. private boolean live=true;
  3. class Heart{
  4. public void jump(){
  5. //直接访问外部类成员
  6. if(live){
  7. System.out.println("心脏在跳动");
  8. }else{
  9. System.out.println("心脏不跳了");
  10. }
  11. }
  12. }
  13. public boolean isLive(){
  14. return live;
  15. }
  16. public void setLive(boolean live){
  17. this.live=live;
  18. }
  19. }

定义测试类

  1. public class InnerDemo{
  2. public static void main(String[] args){
  3. //创建外部类对象
  4. Person p= new Person();
  5. //创建内部类对象
  6. Person.Heart heart = new Person().new Heart(); //通过外部类访问内部类来创建
  7. //调用内部类方法
  8. heart.jump();
  9. //调用外部类方法
  10. p.setLive(false);
  11. //调用内部类方法
  12. heart.jump();
  13. }
  14. }
  15. 输出结果:
  16. 心脏在跳动
  17. 心脏不跳了

内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件但是前面冠以外部类的类名和¥符号。 比如,Person$Heart.class

重名
  1. public class Outer {
  2. int num=10; //外部类 成员变量
  3. public class Inner{
  4. int num=20; //内部类 成员变量
  5. public void methodInner(){
  6. int num=30; //内部类方法 局部变量
  7. System.out.println(num); //局部变量 ->就近原则
  8. System.out.println(this.num);//内部类 局部变量
  9. System.out.println(Outer.this.num);
  10. //外部类成员变量,需要外部类前缀加上this
  11. }
  12. }
  13. }
public class Demo01InnerClass {

    public static void main(String[] args) {
        //外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
        Outer.Inner obj=new Outer().new Inner();
        obj.methodInner();
    }
}
输出结果
30
20
10

2.局部内部类

定义在类的成员方法

特点

1.不能使用任何权限修饰符

2.成员内部类访问包含外部类的变量必须有final修饰;

从java8开始,只要局部变量事实不变,final关键字可以省略

3.外部类只能通过方法访问局部内部类

class Outer{
    int num=10;

    public void mtehodOuter(){
        final int num2=20;
        class Inner{//成员内部类
            public void methodInner(){
                System.out.println(num);//直接访问外部类的成员变量【即使是private】
                System.out.println(num2);//访问外部类方法中的局部变量需要final关键字修饰【除非在生命周期中不变】
            }
        }
    }
}

1.3 匿名内部类【重点】

使用情况

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

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

前提

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

格式

new 父类名或者接口名(){
    //方法重写
    @Override
    public void method(){
        //执行语句
    }
};
public class Demo01test {
    public static void main(String[] args) {

            //正常通过创建实现类来实现接口的方法
//        MyInterface impl= new MyInterfaceImpl();
//
//        impl.method();

        //使用匿名内部类【直接通过接口名创建匿名内部类】
        MyInterface obj=new MyInterface() {
            @Override
            public void method() {
                System.out.println("匿名内部类重写接口方法");
            }
        };
        obj.method();
    }
}

注意事项:

对格式new 接口名称() {...}进行解析:

1.new代表创建对象的动作

2.接口名称就是匿名内部类需要实现哪个接口

3.{...}这才是匿名内部类的内容

匿名对象

new MyInterface(){
            @Override
            public void method1() {
                System.out.println("匿名对象重写接口方法");
            }

            @Override
            public void method2() {
                System.out.println("方法2");
            }    
}.method1();

输出:
匿名对象重写接口方法

但是如果无法再调用method2,否则需要创建一个带对象名的匿名内部类

匿名对象和匿名内部类的区别
  • 匿名内部类是省略了【实现类/子类名称】
  • 匿名对象是省略了【对象名称】

四、引用类型用法总结

4.1 class作为成员变量

我们在定义一个类的时候

public class Hero {
    private String name;//英雄名字
    private int age;//英雄年龄
}

String name;作为英雄名字,而String本身就是一个引用类型,那么我们定义一个武器类,能不能作为Hero类的成员变量呢?

创建一个类

创建武器类Weapon

public class Weapon {
    private String code;

    public Weapon(){

    }
    public Weapon(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }


}

将该类作为成员变量加入类中

将Weapon类作为成员变量放入Hero类中,并完成构造和get set

定义一个攻击的方法来输出name age 和weapon【这里通过weapon.getCode()】来获取武器信息

public class Hero {
    private String name;//英雄名字
    private int age;//英雄年龄
    private Weapon weapon;//武器

    public Hero() {
    }

    public Hero(String name, int age, Weapon weapon) {
        this.name = name;
        this.age = age;
        this.weapon = weapon;
    }
    public void attack(){
        System.out.println("年龄为"+age+"的"+name+"正在用"+
                weapon.getCode()+"攻击敌方");
    }

    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 Weapon getWeapon() {
        return weapon;
    }

    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

在测试类中创建对象并使用

public class Demomain {
    public static void main(String[] args) {
        //创建一个英雄角色
        Hero hero=new Hero();

        //给英雄起一个名字,并且设置年龄
        hero.setName("盖伦");
        hero.setAge(20);

        //创建武器对象
        Weapon weapon=new Weapon("多兰剑");
        //将武器给英雄
        hero.setWeapon(weapon);

        hero.attack();
    }
}
输出结果
年龄为20的盖伦正在用多兰剑攻击敌方

4.2 interface作为成员变量

创建一个释放技能的接口

public interface Skill {
    void use();//释放技能的抽象方法;
}

创建英雄类

public class Hero {
    private String name;
    private Skill skill;

    public Hero() {
    }

    public Hero(String name, Skill skill) {
        this.name = name;
        this.skill = skill;
    }
    public void attack(){
        System.out.println("英雄"+name);
        skill.use();//调用接口中的方法
        System.out.println("技能释放完毕");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }
}

定义测试类

public class DemoGame {
    public static void main(String[] args) {
        Hero hero=new Hero();
        hero.setName("艾希");

        //设置英雄技能
        //1.通过实现类来定义技能接口
//        hero.setSkill(new Skillimpl());

        //2.使用匿名内部类
//        Skill skill=new Skill() {
//            @Override
//            public void use() {
//                System.out.println("通过匿名内部类释放了技能");
//            }
//        };
//        hero.setSkill(skill);


        //3.同时使用匿名内部类和匿名对象
        hero.setSkill(new Skill() {
            @Override
            public void use() {
                System.out.println("同时使用匿名内部类和匿名对象释放方法");
            }
        });
        hero.attack();
    }
}

三种方法中。第三种最为简便

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

import java.util.ArrayList;
import java.util.List;
/*
java.util.List正是ArrayList所实现的接口
*/

public class DemoInterface {

    public static void main(String[] args) {
        //左边是接口名称,右边是实现类名称,这就是多态写法
        List<String> list= new ArrayList<>();
        List<String> result=addNames(list);
        for (int i = 0; i < result.size(); i++) {
            System.out.println(result.get(i));
        }
    }

    public static List<String> addNames(List<String> list){
        list.add("**");
        list.add("雨落");
        return list;
    }
}