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)。
- 外部类:public/(default)
- 成员内部类:pubic/protected / (default) / private
- 局部内部类:什么都不写。
第三章 内部类
3.1 概述
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系 汽车和发动机的关系
分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
成员内部类
格式:定义在类中方法外的类。
定义格式:
修饰符 class 外部类名称{ 修饰符 class 内部类名称{ // ... } // ... }
注意:内用外,随意访问,外用内,需要内部类对象。
使用内部类格式
间接使用:在外部类的方法中,使用内部类,然后main方法调用外部类的方法。
直接方法
公式:外部类名称.内部类名称 对象名 = 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关键字可以省略。
原因:
- new出来的对象在堆内存当中。
- 局部变量跟着的是方法,在栈内存中。
- 方向运行结束之后,立刻出栈,局部变量就会立刻消失。
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
3.2 匿名内部类【重点】
前提
匿名内部类必须继承一个父类或者实现一个接口
如果接口的实现类(或者父类的子类)只需要使用唯一的一次。
使用条件
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】
格式
接口名称 对象名 = new 接口名称(){ //覆盖从写所有抽象方法 }; 对象名.方法名称调用 // 或 new 接口名称(){ //覆盖从写所有抽象方法 }.方法名称调用;
格式解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现哪个接口
- (...)这是匿名内部类的内容
注意问题:
匿名内部类,在【创建对象】的时候,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样,就没有必要用匿名内部类了
匿名对象,在【调用方法】的时候,只能用唯一的一次。
如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
匿名内部类是省略了【实现类 / 子类名称】,但是匿名对象省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事。
代码如下
接口:
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; } }
接口作为参数时,传递它的子类对象。
接口作为返回值类型时,返回它的子类对象
来源:https://www.cnblogs.com/anke-z/p/12444754.html