一,继承
在父子类的继承关系中,创建子类对象,访问成员方法的规则:创建的对象是谁,就优先用谁,如果没有就向上找。无论是成员方法还是成员变量,如果没有都是向上找,绝对不会向下找子类的。
继承关系中,父子类构造方法的访问特点:子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造;子类构造可以通过super关键字来调用父类重载构造;super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造;所以子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super调用,super只能有一个,还必须是第一个。
super关键字的用法有三种:在子类的成员方法中,访问父类的成员变量;在子类的成员方法中,访问父类的成员方法;在子类的构造方法中,访问父类的构造方法。
super关键字用来访问父类内容,而this关键字用来访问本类内容。用法也有三种:在本类的成员方法中,访问本类的成员变量;在本类的成员方法中,访问本类的另一个成员方法;在本类的构造方法中,访问本类的另一个构造方法(在第三种用法中要注意:this(...)调用也必须是构造方法的第一个语句,唯一一个;super和this两种构造调用,不能同时使用)。
二,抽象类
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。抽象类:抽象方法所在类,必须是抽象类才行,在class之前写上abstract即可。
如何使用抽象类和抽象方法:不能直接创建new抽象类对象;必须用一个子类来继承抽象父类;子类必须覆盖重写抽象父类当中所有的抽象方法(覆盖重写:去掉抽象方法的abstract关键字,然后补上方法体大括号);创建子类对象进行使用。
在子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
案例:群主发普通红包
发红包逻辑:三要素:返回值类型:ArrayList<Integer> ;方法名称:send;参数列表:总共发多少钱 int totalMoney,分成多少份int count
收红包逻辑:三要素:返回值类型:void;方法名称receive;参数列表:ArrayList<Integer>
(1)先定义一个User类
public class User {
private String name;//姓名
private int money;//余额
name和money的getter和setter方法
public User(String name, int money) {
this.name = name;
this.money = money;
}
public User() {}
//展示一下当前用户有多少钱
public void show(){
System.out.println("我叫:"+name +"我有多少钱:" +money);
}
}
(2)定义一个群主类
public class Manager extends User {
public Manager(){}
public Manager(String name, int money) {
super(name, money);
}
public ArrayList send(int totalMoney, int count){
//需要一个集合存储若干个红包的金额
ArrayList redList =new ArrayList<>();
//看下群主自己有多少钱
int leftMoney =super.getMoney();
if(totalMoney > leftMoney){
System.out.println("余额不足");
return redList;//返回空集合
}
//扣钱,实际是重新设置余额
super.setMoney(leftMoney - totalMoney);
//发红包需要平均拆分为count份
int avg = totalMoney / count;
int mod = totalMoney % count;//甩下的零头
//除不开的零头,包在最后一个红包中,下边把红包一个一个放到集合中
for (int i =0; i < count-1; i++) {
redList.add(avg);
}
//最后一个红包
int last = avg + mod;
redList.add(last);
return redList;
}
}
(3)定义一个成员类
public class Member extends User {
public Member() {}
public Member(String name, int money) {
super(name, money);
}
public void receive(ArrayList list){
//从多个红包当中随便抽取一个,给我自己
//随机获取一个集合当中的索引编号
int index =new Random().nextInt(list.size());
//根据索引,从集合当中删除,并且得到被删除的红包,给我自己。
int delta = list.remove(index);
//当前成员自己本来有多少钱
int money =super.getMoney();
//加法,并且重新设置回去
super.setMoney(money + delta);
}
}
三,接口
(1)接口的抽象方法
接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract;这两个关键字修饰符可以选择性的省略。
public interface MyInterfaceAbstract {
//以下四个都是抽象方法
public abstract void methodAbs();
abstract void methodAbs2();
public void methodAbs3();
void methodAbs4();
}
接口使用步骤:接口不能直接使用,必须有一个实现类来实现该接口(implements);接口的实现类必须覆盖重写接口中所有的抽象方法;创建实现类的对象,进行使用。
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
(2)接口的默认方法
格式:public default 返回值类型 方法名称(参数列表){方法体}
可以解决接口升级问题。
接口的默认方法,可以通过接口实现类对象,直接调用。接口的默认方法也可以被接口实现类进行覆盖重写。
(3)接口的静态方法
格式:public static 返回值类型 方法名称(参数列表){方法体}
不能通过接口实现类的对象来调用接口当中的静态方法。通过接口名称直接调用其中的静态方法。格式:接口名称.静态方法名(参数)。
(4)接口的私有方法
从Java9开始,接口当中允许定义私有方法。普通私有方法,解决多个默认方法之间重复代码问题。格式:private 返回值类型 方法名称(参数列表){方法体} 静态私有方法,解决多个静态方法之间重复代码问题。格式:private static 返回值类型 方法名称(参数列表){方法体}
(5)接口中定义成员变量
必须使用public static final三个关键字进行修饰,从效果上看,这其实就是接口的常量。格式:public static final 数据类型 常量名称 = 数据值;接口当中的常量,可以省略public static final ,必须进行赋值,接口中常量的名称,使用完全大写的字母,用下划线进行分隔。
使用接口的时候要注意:
接口是没有静态代码块或构造方法的。一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。格式:public class MyInterfaceImp implements MyInterfaceA, MyInterfaceB{}。如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可。一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。
类和类之间是单继承的,直接父类只有一个;类和接口之间是多实现的,一个类可以实现多个接口;接口和接口之间是多继承的。注意: 多个父接口当中的抽象方法如果重复,没关系;多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且带着default关键字。
四,多态
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。格式:父类名称 对象名=new 子类名称(); 或 接口名称 对象名 =new 实现类名称()
访问成员变量的两种方式:一,直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。二是间接通过成员方法访问:看该方法属于谁,优先用谁,没有则向上找。口诀:编译看左边,运行还看左边。
访问成员方法的方式:看new的是谁,就优先用谁,没有则向上找。口诀:编译看左,运行看右。
对象的向上转型,其实就是多态写法:格式:父类名称 对象名= new 子类名称(); 含义:右侧创建一个子类对象,把它当作父类来看待使用。向上转型一定是安全的。
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。解决方案:用对象的向下转型[还原],格式:子类名称 对象名=(子类名称) 父类对象;含义:将父类对象,[还原]成为本来的子类对象。
如何才能知道一个父类引用的对象,本来是什么子类?格式: 对象 instanceof 类名称 这将会得到一个boolean值结果,也就是判断前边的对象能不能当作后边类型的实例。
五,final关键字
(1)当final关键字用来修饰一个类的时候,格式:public final class 类名称{};含义:当前这个类不能有任何子类。
(2)当final关键字用来修饰一个方法的时候,这个方法就是最终方法,不能被覆盖重写。格式:修饰符 final 返回值类型 方法名称(参数列表){}
(3)final修饰局部变量,这个变量就不能修改,“一次赋值,终生不变”。对基本类型来说,不可变说的是变量中的数据不可改变,对于引用类型来说,不可变说的是变量中的地址值不可改变。
(4)final修饰成员变量,这个变量照样不可变。由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了;对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值;必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
六,权限修饰符
Java中有四中权限修饰符:
public > protected > (default) > private
同一个类 yes yes yes yes
同一个包 yes yes yes no
不同包子类 yes yes no no
不同包非子类 yes no no no
七,内部类
分类:成员内部类和局部内部类(包含匿名内部类)
(1)成员内部类
定义格式:修饰符 class 外部类名称 {
修饰符 class 内部类名称{
}
}
注意:内用外,随意访问;外用内,需要内部类对象。 .class文件名:外部类$内部类
使用:两种方式
一是间接方法,在外部类的方法当中,使用内部类,然后main只是调用外部类的方法。
Body.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("外部类的方法");
new Heart().beat();
}
getter和setter方法 }
}
Demo01InnerClass.java
public class Demo01InnerClass {
public static void main(String[] args) {
Body body =new Body();
body.methodBody();//通过外部类的对象,调用外部类的方法,里边间接再使用内部类Heart
}
}
输出:外部类的方法
心脏跳动
我叫:null
二是直接方式,公式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
Body.Heart heart =new Body().new Heart();
heart.beat();
如果出现了重名现象,格式为:外部类名称.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
}
}
}
(2)局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。“局部”:只有当前所属的方法才能使用它,出了这个方法外边就不能用了。
定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
}
}
}
Outer.java
public class Outer {
public void methodOuter(){
class Inner{//局部内部类
int num =10;
public void methodInner(){
System.out.println(num);
}
}
Inner inner =new Inner();
inner.methodInner();
}
}
定义一个类的时候,权限修饰符规则:
外部类:public / (default); 成员内部类:public / protected / (default) / private; 局部内部类:什么都不能写。
局部内部类的final问题:局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final的。(从Java8+开始,只要局部变量事实不变,那么final关键字可以省略。)原因:new出来的对象在堆内存中,局部变量是跟着方法走的,在栈内存中,方法运行结束后,立刻出栈,局部变量就会立刻消失,但是new出来的对象会在堆中持续存在,直到垃圾回收消失。
匿名内部类:
如果接口的实现类(或是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用匿名内部类。
定义格式:接口名称 对象名 = new 接口名称(){
//覆盖重写所有抽象方法
};
注意:匿名内部类在【创建对象】的时候,只能使用唯一一次;匿名对象,在【调用方法】时,只能调用唯一一次;匿名内部类是省略了实现类/子类名称,但是匿名对象是省略了对象名称。匿名内部类和匿名对象不是一回事。