文章目录
1. final关键字
1.1 概念及四种用法
final关键字代表最终、不可改变的
常见四种用法:
- 可以用来修饰一个类
- 可以用来修饰一个方法
- 还可以用来修饰一个局部变量
- 还可以用来修饰一个成员变量
1.2 修饰一个类
当final关键字用来修饰一个类的时候,格式:
public final class 类名称 {
// ...
}
含义:当前这个类不能有任何的子类,但仍然可以有父类
注意事项:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没有子类)
1.3 修饰一个方法
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写,但是可以正常被子类继承使用
格式:
修饰符 final 返回值类型 方法名称(参数列表) {
// 方法体
}
注意事项:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为有矛盾
1.4 修饰局部变量
一旦使用final来修饰局部变量,那么这个变量就不能进行更改,即“一次赋值,终身不变”
可以先声明再赋值,只需要保证仅有有唯一一次赋值操作即可
不可变:
- 对于基本类型来说,不可变说的是变量当中的数据不可改变
- 对于引用类型来说,不可变说的是变量当中的地址值不可改变,但里面的内容可变(即通过方法改变)
1.5 修饰成员变量
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变
注意事项:
- 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
- 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选其一
- 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值,且不可有其它成员方法改变其值
1.6 配合static使用
表示全局的不允许被修改的内容,常用于一些配置信息
2. 权限
2.1 四种权限修饰符
public > protected > (default) > private
注意事项:(default)并不是关键字"default",而是根本不写
2.2 不同权限的访问能力
public | protected | default(空的) | private | |
---|---|---|---|---|
同一类中 | ✔️ | ✔️ | ✔️ | ✔️ |
同一包中(子类与无关类) | ✔️ | ✔️ | ✔️ | ❌ |
不同包的子类 | ✔️ | ✔️ | ❌ | ❌ |
不同包中的无关类 | ✔️ | ❌ | ❌ | ❌ |
形象化记忆:
- 同一个类:我自己
- 同一个包:我领居
- 不同包子类:我儿子
- 不同包非子类:陌生人
使用权限建议
- 成员变量使用 private ,隐藏细节
- 构造方法使用 public ,方便创建对象
- 成员方法使用 public ,方便调用方法
3. 内部类
3.1 概述
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类
分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
内部类隐藏在外部类之内,更好的实现了信息隐藏
3.2 成员内部类
①定义
格式:
修饰符 class 外部类名称 {
修饰符 class 内部类名称 {
// ...
}
// ...
}
注意事项:
- 内用外,随意访问
- 外用内,需要内部类对象
package ricky.base.day11.demo01;
public class Body { // 外部类
public class Heart { // 成员内部类
public void methodHeart () {
System.out.println("内部类的方法");
System.out.println("我叫" + name); // 正确访问
}
}
private String name;
public void methodBody() {
System.out.println("外部类的方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
内部类生成的 .class 文件中间以 $ 符号标识,故在给 .java 文件命名时不建议使用 $ 符号
②使用
间接方式:在外部类的方法当中,使用内部类,然后main只是调用外部类的方法
外部类对象.获取方法
直接方式——公式:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
外部类名称.内部类名称 对象名 = 外部类对象.new 内部类名称();
Demo01InnerClass
package ricky.base.day11.demo01;
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body();
body.setName("Ricky");
// 间接方式
body.methodBody();
System.out.println("============");
// 直接方式
Body.Heart heart = new Body().new Heart();
heart.methodHeart();
}
}
Body
package ricky.base.day11.demo01;
public class Body { // 外部类
public class Heart { // 成员内部类
public void methodHeart () {
System.out.println("内部类的方法");
System.out.println("我叫" + name); // 正确访问
}
}
private String name;
public void methodBody() {
System.out.println("外部类的方法");
new Heart().methodHeart(); // 间接
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
③变量访问
如果出现了重名现象,那么访问外部类的成员变量格式为:外部类名称.this.外部类成员变量名
package ricky.base.day11.demo02;
public class Outer {
int num = 10; // 外部类的成员变量
public class Inner {
int num = 20; // 内部类的成员变量
public void methodInner() {
int num = 30; // 内部方法的局部变量
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 内部类的成员变量
System.out.println(Outer.this.num); // 外部类的成员变量
}
}
}
④注意事项
- 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
- 内部类的访问修饰符可以任意,但是访问范围会受到影响
- 内部类可以直接访问外部类的成员,如果出现同名属性,优先访问内部类中定义的
- 可以使用
外部类.this.成员
的方式,访问外部类中的同名信息 - 外部类访问内部类信息,需要通过内部类实例,无法直接访问
- 内部类编译后.class文件命名:外部类$内部类.class
3.3 静态内部类
①定义
静态内部类对象可以不依赖于外部类对象直接创建
修饰符 class 外部类名称 {
修饰符 static class 内部类名称 {
// ...
}
// ...
}
②使用
外部类名称.内部类名称 对象名 = new 外部类名称.内部类名称();
③注意事项
- 静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,可以通过对象实例:
new 外部类.非静态成员
- 静态内部类对象实例时,可以不依赖于外部类对象
- 可以通过
外部类.内部类.静态成员
的方式,访问内部类中的静态成员 - 当内部类属性与外部类属性同名时,默认直接调用内部类中的成员
- 如果需要访问外部类中的静态属性,则可以通过
外部类.属性
的方式 - 如果需要访问外部类中的非静态属性,则可以通过
new 外部类().属性
的方式
3.4 局部内部类(方法内部类)
①定义
如果一个类是定义在一个方法内部的,那么这就是一个局部的内部类
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了,即需要在方法内部创建对象或者返回内部的方法调用
定义格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
// ...
}
}
}
Outer
package ricky.base.day11.demo03;
public class Outer {
public void methodOuter() {
class Inner {
int num = 10;
public void methodInner() {
System.out.println(num);
}
}
Inner inner = new Inner();
inner.methodInner();
}
}
DemoMain
package ricky.base.day11.demo03;
public class DemoMain {
public static void main(String[] args) {
Outer obj = new Outer();
obj.methodOuter();
}
}
定义一个类的时候,权限修饰符规则:
- 外部类:public / (default)
- 成员内部类:public / protected / (default) / private
- 局部内部类:什么都不能写
②注意事项
- 定义在方法内部,作用范围也在方法内
- 和方法内部成员使用规则一样,class前面不可以添加public、private、protected、static
- 类中不能包含静态成员
- 类中可以包含final、abstract修饰的成员
③局部内部类的final问题
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final的
注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略(即是有效的final)
原因:
- new出来的对象在堆内存当中
- 局部变量是跟着方法走的,在栈内存当中
- 方法运行结束之后,立刻出栈,局部变量就会立刻消失
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收才会消失,所以会一直需要访问那个局部变量,冲突
3.5 匿名内部类
①定义
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次
那么这种情况下就可以省略掉该类的定义,而改为使用匿名内部类,即将类的定义与类的创建放到一起完成
定义格式:
接口名称 对象名 = new 接口名称() {
// 覆盖重写了所有抽象方法
}
例一
MyInterface
package ricky.base.day11.demo04;
public interface MyInterface {
void method(); // 匿名方法
}
DemoMain
package ricky.base.day11.demo04;
public class DemoMain {
public static void main(String[] args) {
MyInterface obj = new MyInterface() {
@Override
public void method() {
System.out.println("匿名内部类覆盖重写了方法");
}
};
obj.method();
}
}
例二
Person
package com.ricky.anonymous;
public abstract class Person {
private String name;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void read();
}
PersonTest
package com.ricky.test;
import com.ricky.anonymous.Person;
public class PersonTest {
public void getRead(Person person) {
person.read();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
PersonTest test = new PersonTest();
test.getRead(new Person() {
@Override
public void read() {
// TODO Auto-generated method stub
System.out.println("男生正在看书");
}
});
}
}
3.8 匿名内部类的注意事项
对格式”new 接口名称() { … }“进行解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现哪个接口
- { … }这才是匿名内部类的内容
注意事项:
-
匿名内部类没有类型名称、实例对象名称
-
编译后的文件命名:外部类$数字.class
-
无法使用public、private、protected、abstract、static修饰
-
无法编写构造方法,可以添加构造代码块实现初始化
-
不能出现静态成员
-
匿名内部类可以实现接口也可以继承父类,但不可兼得
-
匿名内部类在创建对象的时候,只能使用唯一一次
如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了
-
匿名对象在调用方法的时候,只能使用唯一一次
如果希望同一个对象调用多次方法,那么必须给对象起个名字
-
匿名内部类是省略了实现类/子类名称,但是匿名对象是省略了对象名称,两者不是一回事!!!
4. 引用类型
4.1 class作为成员变量
武器类
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();
}
}
类作为成员变量时,对它进行赋值的操作,实际上是赋给它该类的一个对象
4.2 interface作为成员变量
接口
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 static List<Integer> getEvenNum(List<Integer> list) {
// 创建保存偶数的集合
ArrayList<Integer> evenList = new ArrayList<>();
// 遍历集合list,判断元素为偶数,就添加到evenList中
for (int i = 0; i < list.size(); i++) {
Integer integer = list.get(i);
if (integer % 2 == 0) {
evenList.add(integer);
}
}
/*
返回偶数集合
因为getEvenNum方法的返回值类型是List,而ArrayList是List的子类,
所以evenList可以返回
*/
return evenList;
}
方法调用
public class Test {
public static void main(String[] args) {
// 创建ArrayList集合,并添加数字
ArrayList<Integer> srcList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
srcList.add(i);
}
/*
获取偶数集合
因为getEvenNum方法的参数是List,而ArrayList是List的子类,
所以srcList可以传递
*/
List list = getEvenNum(srcList);
System.out.println(list);
}
}