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)。
- 外部类: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;
}
}
接口作为参数时,传递它的子类对象。
接口作为返回值类型时,返回它的子类对象