一、final关键字
1.1 final关键字的作用
继承中有提到子类可以继承父类的成员方法和public变量,如果我们不希望子类继承并随意改写父类的某些内容,我们就可以使用Java提供的final关键字,用于修饰不可变的内容。
final:不可改变,可修饰类,方法,变量
- 修饰类时:被修饰的类不可被继承
- 修饰方法时:被修饰的方法不能被重写
- 修饰变量时:被修饰的变量,不能被重新赋值
1.2 final关键字的使用格式
1.2.1 修饰类
查询API时一些类比如String,Math,Scanner等,都是被final修饰的,我们只能使用,不能继承或者更改其内容。
final class 类名{
//方法体
}
/*
1、final关键字修饰一个类
格式:
public final class 类名称{}
含义:当前类不能有任何子类(太监类)
如果一个类是final的,那么类里面的成员方法无法被覆盖重写
*/
public final class MyClass {
public void method(){
System.out.println("方法执行!");
}
}
/*
不能使用final关键字修饰的类作为父类
*/
public class MySubClass /*extends MyClass*/{
}
1.2.2 修饰方法
修饰符 final 返回值类型方法名(参数列表){
//方法体
}
/*
2、final修饰一个方法,这个方法就是最终方法,不能被覆盖重写
注意事项:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾
*/
public class Father {
public void methodFather(){
System.out.println("父类方法执行");
}
public final void method(){
System.out.println("父类final方法执行");
}
}
public class Son extends Father{
@Override
public void methodFather() {
System.out.println("子类覆盖重写了父类的方法");
}
//public void method(){}
}
1.2.3 修饰变量
1、局部变量——基本类型
只能赋值一次,后面不能进行更改,实例如下:
//3、final修饰一个局部变量,修饰后就不可更改,和常量一样
final int num =10;
System.out.println(num);
//num = 20;
//不可变对于基本类型来说是变量中的数据不可变
//对于引用类型来说,不可变是指变量当中的引用类型不可变,但是内容是可以变的
2、局部变量——引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改,实例如下:
3、成员变量
成员变量被修饰时需要进行初始化初始化方式有两种
(1)直接给出初始值
(2)在构造方法中对成员变量进行赋值
/*
4、final修饰一个成员变量
1、对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变
2、对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值
3、必须保证所有的构造方法对final修饰的变量进行赋值
*/
public class Person {
private final String firstName = "yu";
private final String name;
private final int num = 20;
public Person(String name) {
this.name = name;
}
public Person() {
this.name = "余想";
}
public String getName() {
return name;
}
// public void setName(String name) {
// this.name = name;
// }
}
二、权限修饰符
2.1 权限修饰符有哪些?
public > protected > default >private
2.2 不同修饰符的权限
三、内部类
3.1 什么是内部类
将类A定义在类B中,里面的那个类A就是内部类,类B是外部类;
此时根据类A在类B中的位置,可以分为成员内部类和局部内部类(包含匿名内部类)
3.1.1 成员内部类
成员内部类是定义在类中方法外的类
/*
分类:
1、成员内部类
2、局部内部类(包含匿名内部类 )
成员内部类的定义格式
修饰符 class 外部类名称{
修饰符 class 内部类名称{
}
}
注意内用外可以随意访问,外用内,一定需要接用内部对象
======================================
如何使用成员内部类?
有两种方式
1、间接方式:在外部类的方法当中使用内部类,然后main只是调用外部类的方法
2、直接方式:公式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
*/
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body();//外部类的对象
//通过外部类对象调用外部类方法,里面间接使用内部类Heart
body.methodBody();
System.out.println("==================");
//直接方式:直接创建
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
public class Body {//外部类
public class Heart{//内部类
//内部类方法
public void beat(){
System.out.println("心脏跳动");
System.out.println("我叫"+name);
}
}
//外部类成员变量
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//外部类方法
public void methodBody(){
System.out.println("外部类的方法");
new Heart().beat();
}
}
3.1.2 局部内部类
局部内部类是定义在类中方法里面的类,其访问特点是只有当前所属方法才能使用它,出了这个方法外面就不能用了
//实例1:
/*
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
从java8+开始,只要局部变量事实不变,那么final关键字可以省略
原因:
1、new出来的对象在堆内存当中
2、局部变量是跟着方法走的,在栈内存中
3、方法运行结束后,立刻出战,局部变量就会消失
4、但是new出来的对象会在堆内存当中持续存在,直到垃圾回收消失
类活得比变量长,所以变量需要声明成final
*/
public class MyOuter {
public void methodOuter(){
int num = 10;//所在方法的局部变量
// num = 20;
class MyInner{
public void methodInner(){
System.out.println(num);
}
}
}
}
//实例二:
/*
如果一个类是定义在方法内部的,那么这就是一个局部内部类
“局部”:只有当前所属方法才能使用它,出了这个方法外面就不能用了
*/
public class Outer {
public void methodOuter(){
class Inner{//局部内部类
int num =10;
public void methodInner(){
System.out.println(num);
}
}
Inner inner = new Inner();
inner.methodInner();
}
}
3.1.3局部内部类中的匿名内部类
匿名内部类是内部类的简化写法,其本质是实现父类或父接口的匿名的子类对象。
应用场景:当我们只使用父类接口的方法时,若只调用一次,可以采取匿名内部类的形式来调用,可以简化原来的调用方法步骤。
格式:
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
实例:
//父类接口
public interface MyInterface {
void method();//抽象方法
}
//测试类
/*
如果接口的实现类或者父类的子类只需要使用唯一的一次
那么这种情况下就可以省略掉该类的定义,而改为【匿名内部类】
匿名内部类的定义格式
接口名称 对象名 = new 接口名称(){
//覆盖重写所有抽象方法
}
对格式“new 接口名称(){。。。}”进行解析:
1、new代表创建对象的动作
2、接口名称就是匿名内部类需要实现那个接口
3、{。。。}才是匿名内部类的内容
另外还要注意几个问题
1、匿名内部类在创建对象时只能使用唯一的一次
如果希望多次创建对象,而且类的内容一样的话,那么必须使用单独定义的实现类了
2、匿名对象,在调用方法的时候只能调用一次,调用多次还是得声明对象
3、匿名内部类是省略了实现类/子类名称,但匿名对象省略的是对象名称
*/
public class DemoMain {
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 MyInterface(){
@Override
public void method() {
System.out.println("匿名内部类");
}
}.method();
}
}
四、引用类型的用法总结
在Java中,基本类型可以作为成员变量、方法的参数、方法的返回值,那么对应的,引用方法也都可以
4.1 class作为成员变量
应用场景:类A作为类B的成员变量出现;
实例:Weapon类作为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;
}
}
// Hero类
//游戏中的英雄名字
public class Hero {
private String name;
private Weapon weapon;//武器
private int age;//年龄
public Hero() {
}
public Hero(String name, Weapon weapon, int age) {
this.name = name;
this.weapon = weapon;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//测试类
public class DemoMain {
public static void main(String[] args) {
//创建一个英雄角色
Hero hero = new Hero();
hero.setAge(12);
hero.setName("yx");
Weapon weapon = new Weapon("111");
hero.setWeapon(weapon);
System.out.println("英雄名:"+hero.getName()+" 英雄年龄:"+hero.getAge()+" 英雄的武器代号:"+hero.getWeapon().getCode());
}
}
4.2 接口作为成员变量
和class作为成员变量的原理是类似的,接口作为成员变量出现可以使得接口所在的类具有很好的方法拓展性。
实例:
//Skill接口
public interface Skill {
void use();//释放技能的抽象方法
}
//接口实现类
public class SkillImpl implements Skill{
@Override
public void use() {
System.out.println("biubiubiu");
}
}
//Hero类
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("yx");
// Skill impl = new SkillImpl();
//
// hero.setSkill(impl);
//可以搞成匿名类
// Skill skill = new Skill(){
//
// @Override
// public void use() {
// System.out.println("biubiubiu");
// }
// };
// hero.setSkill(skill);
//还可以使用匿名对象和匿名类
hero.setSkill(new Skill(){
@Override
public void use() {
System.out.println("biubiubiu");
}
});
hero.attack();
}
}
4.3 接口作为方法参数和返回值类型
接口作为返回值时,返回的是它的子类对象;
接口作为参数时,传递的是它的子类对象。
实例:
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的子类对象ArrayList
List<String> result = addNmae(list);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static List<String> addNmae(List<String> list){
list.add("aaa");
list.add("bbb");
//接口作为返回值,实际返回的是它的子类对象ArrayList
return list;
}
}