回到目录
final关键字代表最终、不可改变的
当final关键字用来修饰一个类的时候,不能使用final类来作为父类。
final 修饰类的格式:
public final class 类名称 {
// ...
}
含义:当前这个类不能有任何的子类。(太监类)
注意:一个类如果是final修饰的,那么其中所有的成员方法都无法进行重写(因为final修饰的类没有子类来继承,所以无法重写)
当final关键字用来修饰一个成员方法的时候,这个方法就是最终的方法,也就是不能被所在的类的子类重写。
final 修饰成员方法的格式:
修饰符 final 返回值类型 方法名称(参数列表) {
// 方法体
}
注意事项:
对于类、方法来说,abstract关键字和final关键字不能同时使用。原因:抽象方法没有方法体,子类一定要重写抽象方法的;final修饰的方法不能被子类重写,所以abstract和final关键字不能同时使用。
一旦使用final用来修饰局部变量,那么这个变量就不能进行更改,“一次赋值,终生不变”,就算第二次赋值跟前一次赋值一样也不行!只能保证只有唯一的一次赋值。
final int num2 = 200;
// num2 = 250; // 错误写法!不能改变!
// num2 = 200; // 错误写法!就算跟原来的值一样,也不可以。只能进行唯一的一次赋值!
对于成员变量来说,如果使用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) { // setter赋值要去掉
// this.name = name;
// }
}
回到目录
Java中有四种权限修饰符:
public > protected > (default) > private
同一个类(我自己)访问 YES YES YES YES
同一个包(我邻居)访问 YES YES YES NO
不同包但是子类(我儿子)(要导包) YES YES NO NO
不同包不是子类(陌生人)(要导包) YES NO NO NO
注意事项:(default)并不是关键字“default”,而是根本不写。
回到目录
内部类:一个类内部包含另一个类。
相当于,一个事物的内部包含另一个事物。例如:汽车和发动机的关系;身体和心脏的关系。
分类:
成员内部类的定义格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{
// ...
}
// ···
}
注意:
使用成员内部类的两种方式
示例
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 class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body(); // 外部类的对象
// 间接使用,通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
body.methodBody();
// 直接使用,按照公式写:
Body.Heart heart = new Body().new Heart();
// 外.内 = new 外.new 内
heart.beat();
}
}
如果外部类和内部类有重名的成员变量变量,那么,在内部类中,访问外部类成员变量的格式是:【外部类名称.this.外部类成员变量名】
内部类与外部类如果出现了重名成员变量的现象,那么访问外部类重名的成员变量的格式是:外部类名称.this.外部类成员变量名
public class Outer {
int num = 10; // 外部类的成员变量
public class Inner {
int num = 20; // 内部类的成员变量,正好重名了
public void methodInner() {
int num = 30; // 内部类方法的局部变量
System.out.println(num); // 局部变量,就近原则,30
System.out.println(this.num); // 内部类的成员变量,20
System.out.println(Outer.this.num); // 外部类的成员变量,10
// 外部类名称.this.外部类成员变量名:在内部类里访问与外部类重名的成员变量
}
}
}
局部内部类:在方法里面定义一个类。
“局部”:只有当前所属的方法才能使用这个类,出了这个方法外面就不能用这个类了。
局部内部类定义格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
// ...
}
}
}
只有在局部内部类所在的方法中,才可以使用这个类,出了这个方法就不可以使用了。使用方法,与其他类一样,先要根据类new一个对象.
class Outer {
public void methodOuter() {
class Inner {
// 局部内部类,权限修饰符什么都不能写
int num = 10;
public void methodInner() {
System.out.println(num); // 10
}
}
Inner inner = new Inner(); // 根据局部内部类创建一个对象
inner.methodInner();
}
}
public > protected > (default) > private
定义一个类的时候,权限修饰符使用的规则:
权限修饰符,要根据是哪一种类,来选择用哪一种权限修饰符!
局部内部类,如果希望访问它所在方法的局部变量,那么这个局部变量必须是【有效final的】。
备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。
原因:
public class MyOuter {
public void methodOuter() {
/*final*/ int num = 10; // 所在方法的局部变量,final可以省略
class MyInner {
public void methodInner() {
System.out.println(num); // 局部内部类访问所在方法的局部变量,这个局部变量必须是有效final的,只能赋值唯一的一次
}
}
}
}
回到目录
匿名内部类 :是内部类的简化写法。它的本质是一个带有具体实现的父接口(或者父类)的匿名的子类对象。
当我们使用接口的时候,需要以下的步骤:
但是,我们的目的,最终只是为了调用方法,如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那些步骤就显得过于麻烦了。
那么这种情况下,就可以省略该实现类的定义,而改为使用【匿名内部类】。
大括号范围是【没有名字的类】+【又是局部内部类】,所以是匿名内部类。
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
// 接口原本是不能直接new的。接口不能直接使用,必须有一个“实现类”来“实现”该接口
// 注意:这个大括号就相当于是一个实现类(没有名字),实现了该接口,所以要重写接口的抽象方法。
// 大括号里面,要重写 new的这个接口里的所有抽象的方法
}; // 分号不可以省略!
对格式“new 接口名称(){···}”进行解析:
接口:
public interface MyInterface {
void method1(); // 抽象方法,public static可以省略
void method2();
}
不用定义实现类MyInterfaceImpl,通过匿名内部类来使用接口:
public class DemoMain {
public static void main(String[] args) {
// MyInterface obj = new MyInterfaceImpl(); 这个是多态的写法,左父右子,但是要创建一个实现类来实现该接口
// obj.method();
// MyInterface some = new MyInterface(); // 错误写法!不能直接new接口!
// 使用【匿名内部类】,但【不是匿名对象】,对象名称就叫objA
MyInterface objA = new MyInterface() {
// 通过匿名内部类,创建了对象,对象名就是objA
@Override // 这里面就是匿名内部类了,要重写接口的抽象方法
public void method1() {
System.out.println("匿名内部类实现了方法!111-A");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-A");
}
}; // 这个分号是不能省略的
objA.method1(); // 通过匿名内部类创建的对象,有对象名,可以通过对象引用来调用匿名内部类里的所有方法
objA.method2();
// 使用了【匿名内部类】,而且省略了对象名称,【也是匿名对象】
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method1();
// 因为【匿名对象】无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象来调用另一个方法
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法!222-B");
}
}.method2();
}
}
回到目录
成员变量可以是基本类型,也可以是引用类型(自己定义的类都可以)
英雄类:
// 游戏当中的英雄角色类
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() + "攻击敌方。");
} // 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 Weapon {
private String code; // 武器的代号
public Weapon() {
// 无参构造
}
public Weapon(String code) {
// 有参构造
this.code = code; // 把传入的局部变量赋给本类的成员变量code
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
使用:
public class DemoMain {
public static void main(String[] args) {
// 根据Hero类,创建一个英雄角色
Hero hero = new Hero();
// 为英雄起一个名字,并且设置年龄
hero.setName("盖伦");
hero.setAge(20);
// 根据Weapon类,创建一个武器对象
Weapon weapon = new Weapon("AK-47"); // 调用有参构造
// 为英雄配备武器
hero.setWeapon(weapon); // weapon = new Weapon("AK-47")
// 年龄为20的盖伦用多兰剑攻击敌方。
hero.attack();
}
}
回到目录
// 技能接口Skill:
public interface Skill {
void use(); // 施放技能的抽象方法
}
// 英雄类:
public class Hero {
private String name; // 英雄的名称
private Skill skill; // Skill是一个接口,作为Hero类的成员变量
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 SkillImpl implements Skill {
@Override
public void use() {
System.out.println("Biu~biu~biu~");
}
}
使用:
public class DemoGame {
public static void main(String[] args) {
Hero hero = new Hero();
hero.setName("艾希"); // 设置英雄的名称
// 设置英雄技能
// hero.setSkill(new SkillImpl()); // 使用单独定义的实现类
// 还可以改成使用匿名内部类,但不是匿名对象。这样就不用前面的实现了了。
// Skill skill = new Skill() { // 就是多态的写法,大括号里的是一个实现类(要重写接口的抽象方法),向上转型为接口了
// @Override
// public void use() {
// System.out.println("Pia~pia~pia~");
// }
// };
// hero.setSkill(skill);
// 往Hero类里面传入的是匿名内部类(实现类),而且向上转型为接口了,相当于接口类型
// 进一步简化,同时使用匿名内部类和匿名对象
hero.setSkill(new Skill() {
@Override
public void use() {
System.out.println("Biu~Pia~Biu~Pia~");
}
});
hero.attack();
}
}
回到目录
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 = 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("古力娜扎");
list.add("玛尔扎哈");
list.add("沙扬娜拉");
return list;
}
}
注意:在程序中,任何关于钱的计算,都不能使用小数,只能使用整数!因为double也不精确。1块钱叫100分