第一章 final
1.1 final关键字和概念和四种用法
- final关键字代表最终,不可改变的
- 常见四种用法
- 可以用来修饰一个类
- 可以用来修饰一个方法
- 可以用来修饰一个局部变量
- 可以用来修饰一个成员变量
1.2 final关键字用于修饰类
- 格式:
public final class 类名称 { //... }
- 含义:当前这个类不能有任何的子类(太监类)
- 不能使用一个final类来作为父类
- 注:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写(因为没有儿子)
- 注:但是可以覆盖重写final的父类方法
1.3 final关键字用于修饰成员方法
- 当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能覆盖重写(不能覆盖重写父类当中的final方法)
- 格式:
修饰符 final 返回值类型 方法名(参数列表){ //方法体 }
- 注意事项:
- 对于类,方法来说==,abstract关键字和final关键字不能同时使用==,因为矛盾
1.4 final关键字用于修饰局部变量
- 一旦final用来修饰局部变量,那么这个变量就不能进行更改。
- “一次赋值,终身不变”
- 写法:
//方法体中才是局部变量 public static void main(String[] args) { //格式1: final int num1 = 20; //对 //格式2:只要保证唯一一次赋值即可 final int num2; num = 30; }
- 注:对于基本数据类型来说,不可变说的是变量当中的数据不可变
- 注:对于引用数据类型来说,不可变说的是变量当中的地址值不可变
- 但是其中的内容可以改变
//定义一个类:Student.java
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//主方法:Demo01Final.java
public static void main(String[] args) {
Student student = new Student("赵丽颖");
System.out.println(student.getName());
student = new Student("霍建华");
System.out.println(student.getName());
System.out.println("=======");
final Student student1 = new Student("高圆圆");
System.out.println(student1.getName());
//对于引用数据类型来说,不可变说的是变量当中的地址值不可变
//student1 = new Student("渣渣辉");
//但是其中的内容可以改变
student1.setName("渣渣辉");
System.out.println(student1.getName());
}
1.5 final关键字用于修饰成员变量
- 对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变的。
- 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
- 对于final成员变量,要么直接赋值,要么通过构造方法赋值。(二者选一个)
- 构造方法赋值时,都对final成员变量进行赋值,默认参数的也要写,并且不能在写setter
第二章 权限
2.1 四种权限修饰符
- Java中有四种权限修饰符:(访问修饰符)
- public > protected > (default) > private
- 同一个类(我自己):yes,yes,yes,yes
- 同一个包(我邻居):yes,yes,yes,no
- 不同包子类(我儿子):yes,yes,no,no
- 不同包非子类(陌生人):yes,no,no,no
- 注:(default)不能关键字“default”,而是根本不写。
第三章 内部类
3.1 内部类的概念与分类
- 如果一个事物的内部包含另一个事物,那么这既是一个类内部包含另一个类。
- 例如:身体和心脏,汽车和发动机
- 分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
3.2 成员内部类的定义
- 格式:
修饰符 class 外部类名称 { 修饰符 class 内部类名称 { //... } //... }
- 注意事项:
- 内用外,随意访问
- 外用内,一定需要借助内部类对象
- 在编译后生成的.class文件中:
- 外部类Body.class
- 内部类Body$Heart.class
- 所以在命名时候不推荐使用$来隔开,会误认为是内部类。
定义一个类Body.java和内部类Heart.java
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("外部类的方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.3 成员内部类的使用
- 使用内部类的两种方式:
- 间接方式:
- 在外部类的方法当中,使用内部类;
- 然后main只是调用外部类的方法
- 直接方式:(公式)
- 原来:类名称 对象名 = new 类名称();
- 内部类:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();【重点】
- 间接方式:
//定义一个类Body.java和内部类Heart.java
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("外部类的方法");
/*Heart heart = new Heart();
heart.beat();*/
new Heart().beat(); //匿名对象
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//主方法:Demo01InnerClass.java
public static void main(String[] args) {
Body body = new Body();
body.methodBody();
System.out.println("==========");
//按照公式写
Body.Heart beatH = new Body().new Heart();
beatH.beat();
}
//结果:
外部类的方法
心脏跳动:蹦蹦蹦
我叫null
==========
心脏跳动:蹦蹦蹦
我叫null
3.4 内部类的同名变量访问
- 如果出现了重名现象,那么格式:
- 外部类名称.this.外部类成员变量名
//定义一个类Outer.java和内部类Inner.java
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); //外部类的成员变量
}
}
}
//主方法:Demo02InnerClass.java
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.methodInner();
}
//结果:
30
20
10
3.5 局部内部类定义
- 如果一个类是定义在一个方法内部的,那么这就是一个局部内部类
- “局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了
- 定义格式:
修饰符 class 类名称 { 修饰符 返回值类型 外部类方法名称(参数列表) { class 局部内部类 { //... } } }
- 使用局部内部类在外部方法中创建对象
public void methodOuter(){ class Inner { //局部内部类 int num = 10; public void methodInner(){ System.out.println(num); } } Inner inner = new Inner(); inner.methodInner(); }
- 注:局部内部类前面没有修饰符
- 小结一下:类的权限修饰符:
- public > protected > (default) > private
- 定义一个类的时候,权限修饰符规则:
- 外部类:public / (default)
- 成员内部类:public / protected / (default) / private
- 局部内部类:什么都不写(和(default)效果不一样),这里就只能在方法内使用
3.6 局部内部类的final问题
- 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
- 在JDk 1.7之前,在局部内部类所在方法的局部变量必须写final
public class MyOuter { public void methodOuter(){ final int num = 10; //现在final可以不写 class Inner { public void methodInner(){ System.out.println(num); } } } }
- 注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略
- 原因:
- new出来的对象在堆内存当中
- 局部变量是跟着方法走的,在栈内存当中
- 方法运行结束之后,立刻出栈,局部变量就会立刻消失
- new出来的对象会在堆当中持续存在,知道垃圾回收消失
- 所以局部变量生命周期比new对象短,在new对象还想用到局部变量时候,它已经死了,所以需要一个final。
3.7 匿名内部类【重点】
- 使用最多
- Lambda表达式的重要前提
- 如果接口的实现类(或者父类的子类)只需要使用唯一的一次。
- 这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】
- 匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() { //覆盖重写所有抽象方法 };
- 别忘了写最后的;号
{}包括的才是一个没有名字的类
- 别忘了写最后的;号
- 意义:可以省掉一个类的单独定义,(随着用随着就定义)
//定义一个接口:MyInterface.java
public interface MyInterface {
void method(); //这是抽象方法
}
//定义一个实现类:MyInterfaceImpl.java(有了匿名内部类,实现类可以不写了)
public class MyInterfaceImpl implements MyInterface {
@Override
public void method() {
System.out.println("实现类覆盖重写了方法!");
}
}
//主方法:DemoMain.java
public static void main(String[] args) {
//普通写法
/*MyInterfaceImpl impl = new MyInterfaceImpl();
impl.method();*/
//多态写法
/*MyInterface obj = new MyInterfaceImpl();
obj.method();*/
//匿名内部类
MyInterface obj = new MyInterface() {
@Override
public void method() {
System.out.println("匿名内部类实现了方法");
}
};
obj.method();
}
- 有了匿名内部类,实现类可以不写了
3.8 匿名内部类的注意事项
- 对格式右边进行解析:“new 接口名称 {…}”
- new代表:创建对象的动作
- 接口名称:就是匿名内部类需要实现那个接口
- {…}这才是匿名内部类的内容
- 另外还要注意几点问题:
- (1)匿名内部类,在创建对象的时候,只能使用唯一的一次。
- (1)new 两个对象做不到(只能在写一遍),如果希望多次创建对象,而且类的内容一样的话,那么就只能使用单独定义的实现类了
- (2)使用匿名内部类,但不是匿名对象【重点】,对象名就叫obj
MyInterface obj = new MyInterface() {...};
- (2)使用了匿名内部类,而且省略了对象名称,也是匿名对象【重点】
new MyInterface() {...}.method();
- 匿名对象,在【调用方法】的时候,只能调用唯一一次。
- 如果希望同一个对象,调用多次方法,必须给对象起个名字
- (3)匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】——>匿名内部类和匿名对象不是一回事【重点】
//定义一个接口:MyInterface.java
public interface MyInterface {
void method1(); //这是抽象方法
void method2();
}
//DemoMain.java
public static void main(String[] args) {
//普通写法
/*MyInterfaceImpl impl = new MyInterfaceImpl();
impl.method();*/
//多态写法
/*MyInterface obj = new MyInterfaceImpl();
obj.method();*/
//匿名内部类
MyInterface obj = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法1");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法2");
}
};
obj.method1();
obj.method2();
System.out.println("===================");
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法1");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法2");
}
}.method1();
//因为匿名对象没法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法1");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法2");
}
}.method2();
}
//使用匿名内部类就可以省略实现类(在使用一次的情况下)
public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("实现类覆盖重写了方法1!");
}
@Override
public void method2() {
System.out.println("实现类覆盖重写了方法2!");
}
}
第四章 成员变量类型和方法的参数及返回值特例
4.1 类作为成员变量类型
//定义一个英雄类:Hero.java
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;
}
}
//定义一个武器类:Weapon.java(是Hero的成员变量类型)
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;
}
}
//主方法:DemoMain.java
public static void main(String[] args) {
//创建一个英雄角色
Hero hero = new Hero();
//为英雄起一个名字,并且设置年龄
hero.setName("伊利丹");
hero.setAge(20);
//创建一个武器对象
Weapon weapon = new Weapon("霜之哀伤");
//为英雄配备武器
hero.setWeapon(weapon);
hero.attack();
}
4.2 接口作为成员变量类型
//定义一个Hero类:Hero.java
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;
}
}
//定义一个接口:Skill.java
public interface Skill {
void use(); //释放技能的抽象方法
}
//定义一个实现类:SkillImpl.java
public class SkillImpl implements Skill{
@Override
public void use() {
System.out.println("biu~biu~biu");
}
}
//主方法:DemoGame.java
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 接口作为方法的参数或返回值
- 注: java.util.List正是ArrayList所实现的接口
//主方法:DemoInterface.java
public class DemoInterface {
public static void main(String[] args) {
List<String> listA = new ArrayList<>();
List<String> result = addNames(listA);
for (String res : result) {
System.out.println(res);
}
/* 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;
}
}
4.4 发红包案例
- 注:在程序当中和钱有关系一定不能使用浮点型:float,double
- 钱换为整数,单位为分
- 注:Swing源码,接口即图片(相应教学文件中)
- 案例分析:
4.5 发红包案例——普通红包
//RedPacketFrame的子类(因为父类是一个抽象类)
public class MyRed extends RedPacketFrame {
/**
* 构造方法:生成红包界面。
*
* @param title 界面的标题
*/
public MyRed(String title) {
super(title);
}
}
//定义一个普通红包实现类:NormalMode.java
public class NormalMode implements OpenMode {
@Override
public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
ArrayList<Integer> list = new ArrayList<>();
int avg = totalMoney / totalCount; //平均值
int mod = totalMoney % totalCount; //余数,零头
//注意totalCount - 1代表,最后一个先留着
for(int i = 0; i < totalCount - 1; i++){
list.add(avg);
}
//有零头,需要放在最后一个红包当中
list.add(avg + mod);
return list;
}
}
//主方法:Bootstrap.java
public static void main(String[] args) {
//因为RedPacketFrame是抽象类,所以不能new,需要创建子类
MyRed red = new MyRed("红包案例");
//设置群主名称
red.setOwnerName("张子玄");
//创建普通红包对象
OpenMode normal = new NormalMode();
red.setOpenWay(normal);
}
- 结果:
4.6 发红包案例——随机红包
- 随机分配,有可能多,有可能少
- 最少1分,最多不超过“剩下金额平均数的2倍”
- 第一次发红包,随机范围是0.01~6.66
- 第一次发完,至少剩下3.34
- 此时还需要发两个
- 此时再发范围应该就是0.01~3.34(取不到右边,要剩下0.01)——符合随机的左闭右开[)
- 总结一下范围公式:1 + Random.nextInt(leftMoney / leftCount * 2);
- 加一保证随机不为0
- 注:for循环次数不能变要用totalCount
//RedPacketFrame的子类(因为父类是一个抽象类)
public class MyRed extends RedPacketFrame {
/**
* 构造方法:生成红包界面。
*
* @param title 界面的标题
*/
public MyRed(String title) {
super(title);
}
}
//定义一个随机红包实现类:RandomMode.java
public class RandomMode implements OpenMode {
@Override
public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
ArrayList<Integer> list = new ArrayList<>();
//随机分配,有可能多,有可能少
//最少1分,最多不超过“剩下金额平均数的2倍”
//第一次发红包,随机范围是0.01~6.66
//第一次发完,至少剩下3.34
//此时还需要发两个
//此时再发范围应该就是0.01~3.34(取不到右边,要剩下0.01)——符合随机的左闭右开[)
//总结一下范围公式:1 + Random.nextInt(leftMoney / leftCount * 2);
Random r = new Random(); //首先创建一个随机数生成器
//totalMoney是总金额,totalCount是总个数,不变
//额外定义两个变量,分别代表剩下多少钱,剩下多少份
int leftMoney = totalMoney;
int leftCount = totalCount;
//随机发前n-1个,最后一个不需要随机
for(int i = 0; i < totalCount - 1; i++){ //for循环次数不能变要用totalCount
//按照公式生成随机金额
int money = r.nextInt(leftMoney / leftCount * 2) + 1;
list.add(money); //将一个随机红包方法集合
leftMoney -= money; //剩下金额越发越少
leftCount--; //剩下还应该再发的红包个数,递减
}
//最后一个红包不需要随机,直接放进去
list.add(leftMoney);
return list;
}
}
//主方法:Bootstrap.java
public static void main(String[] args) {
//因为RedPacketFrame是抽象类,所以不能new,需要创建子类
MyRed red = new MyRed("红包案例");
//设置群主名称
red.setOwnerName("张子玄");
//创建一个手气红包对象
OpenMode random = new RandomMode();
red.setOpenWay(random);
}
- 结果: