一、final关键字
1.1 概述
学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承 API中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java提供了 final
关键字, 用于修饰不可改变内容
1.2 使用方式
修饰类
final class 类名{
}
查询API发现像 public final class String
、 public final class Math
、 public final class Scanner
等,很多我们学习过的类,都是被final
修饰的,目的就是供我们使用,而不让我们所以改变其内容
修饰方法
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
重写被final
修饰的方法,编译时就会报错
修饰变量
1.局部变量—基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
public class FinalDemo01{
public static void main(String[] args){
//声明变量,使用final修饰
final int a;
//第一次赋值
a=10;
//第二次赋值
a=20;//报错
}
}
2.局部变量—引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的 修改,代码如下:
public class FinalDemo02{
public static void main(String[] args){
//创建User对象
final User u=new User();
//创建 另一个User对象
u= new User(); //报错,
//调用setName方法
u.setName("张三");//可以修改
}
}
3.成员变量
成员变量涉及到初始化的问题,初始化方式有两种,只能二选一
- 显示初始化
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修饰的常量名称,书写规范:所有字母大写
二、权限修饰符
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.成员内部类
成员内部类:定义在类中方法外的类。
class 外部类{
class 内部类{
}
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类 Car
中包含发动机 类 Engine
,这时, Engine
就可以使用内部类来描述,定义在成员位置。
class Car{//外部类
class Engine{//内部类
}
}
访问特点
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
public class Person{
private boolean live=true;
class Heart{
public void jump(){
//直接访问外部类成员
if(live){
System.out.println("心脏在跳动");
}else{
System.out.println("心脏不跳了");
}
}
}
public boolean isLive(){
return live;
}
public void setLive(boolean live){
this.live=live;
}
}
定义测试类
public class InnerDemo{
public static void main(String[] args){
//创建外部类对象
Person p= new Person();
//创建内部类对象
Person.Heart heart = new Person().new Heart(); //通过外部类访问内部类来创建
//调用内部类方法
heart.jump();
//调用外部类方法
p.setLive(false);
//调用内部类方法
heart.jump();
}
}
输出结果:
心脏在跳动
心脏不跳了
内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件但是前面冠以外部类的类名和¥符号。 比如,Person$Heart.class
重名
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
}
}
}
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;
}
}