一、抽象(abstract)类
在一个父类当中,因为每一个子类对应的覆盖方法有不同的表现形式,因此如果该方法在父类当中实现是没有意义的,因为最终会调用在子类中重写的方法。除非在子类中没有重写该方法才会调用父类的该方法。
为了解决这一问题,父类当中的方法没有必要进行实现,但是普通类当中有没有实现的方法编译器又会报错。因此衍生出了抽象方法与抽象类。
抽象类中的规则及注意事项
1)类 抽象类
- 只能声明引用,不能创建对象(无法实例化)
- 供子类去继承,言外之意抽象方法不能是 private 的
- 抽象方法不能是被 final 修饰的,它与 abstract 不能被共存(后面讲)
- 生活中,抽象类是从不同的子类 中抽象出的父类,自身不存在对象,如:动物类,交通工具类
abstract class Shape {
abstract private void draw();
}
// 编译出错
Error: 非法的修饰符组合: abstract和private
2)方法 抽象方法
- 只有方法声明,没有方法实现;
- 写法:方法实现部分用分号代替;abstract public void draw();
- 只声明了对象具有什么功能,没有定义对象如何实现该功能。
3)抽象类和抽象方法的关系
- 如果一个类具有抽象方法,那么这个类就必须是抽象类
- 子类继承抽象类,如果子类不希望也成为抽象类,就必须覆盖父类中的所有的抽象方法(当然抽象类中未必有抽象方法)
抽象方法怎么调用
引用可以调用其引用类型中声明的所有方法,这其中就包括了抽象方法
public class TestAbstract {
public static void main(String[] args) {
Super s =new Sub();
s.method();
}
abstract class Super {
abstract public void method();
}
class Sub extends Super {
public void method(){
System.out.println("Sub method");
}
}
}
// 执行结果 :Sub method
抽象类的作用
- 希望被继承,对于父类没有实现意义的方法让子类进行重写(作用)
- 抽象类可以作为标准,约定了对象应该具备的方法、规格
- 隔离了标准的使用者和标准的实现者,从而实现弱耦合(耦合指的是两个类之间的联系的紧密程度)
强耦合:类之间存在着直接关系
弱耦合:在两个类的中间加入一层,将原来的直接关系变成间接关系,使得两个类对中间层是强耦合,两类之间变为弱耦合)
耦合可以理解是依赖的关系,当我们设计变量类型时,尽可能的与父类建立关系,避免与子类建立联系。
先看这则代码:
父类(台灯)直接和子类(红灯泡)建立了联系,当我们想把红灯泡换成黄灯泡时,父类要重新建立(更改代码复杂)
使用多态与抽象类
将子类共性放到同个父类,相当于中间人。
整体代码
public class TestLight{
public static void main(String[] args) {
Light light = new Light();
RedBulb bulb1 =new RedBulb();
light.setBulb(bulb1);
light.powerOn();
YellowBulb bulb2 =new YellowBulb();
light.setBulb(bulb2);
light.powerOn();
}
class Ligt{
private Bulb bulb =null;
public void setBulb(Bulb bulb){
this bulb = bulb;
}
public void powerOn(){
bulb.shine() ;
}
}
abstract class Bulb {
abstract public void shine();
}
class RedBulb extends Bulb {
public void shine(){
System.out.println("发出红光");
}
}
class YellowBulb extends Bulb {
public void shine(){
System.out.println("发出黄光");
}
}
}
//执行结果
发出红光
发出黄光
二、static关键字
static(静态)作用
1)修饰属性 (内存开辟在方法区中)
2)修饰方法 (只有调用方法时才在栈中开辟内存)
3)代码块
4) 修饰类 (后面讲内部类会讲到)
1)修饰属性:Java静态属性和类相关, 和具体的实例无关。换句话说, 同一个类的不同实例共用同一个静态属性。
共用:表示全类都有,不属于任何对象;可以直接用类名访问(类名.属性)
若需要调用类当中的有static的属性,则只需要类名.属性 即可调用,而不能对象名.属性 来调用,否则编译器会报警告。
被static修饰的成员变量的内存在方法区中开辟,因为它不属于对象因此不在堆上开辟内存;而在方法区中开辟内存开辟且仅开辟一次。
下图体现出对象、类、属性(实例成员变量、静态成员变量)之间的关系。
2)修饰方法
如果在任何方法上应用 static 关键字,此方法称为静态方法。
注意事项:
对第5条的解释:静态方法,以及静态成员,都会比普通类,方法,成员,优先进入内存的!
(后进来的)调用(先进来的),方法,是绝对可以的,理所应当,因为他早早的就进内存了!
它调用自己同类的普通方法,也是可以的,因为他们一起来的!
反过来,你用(静态)去调用(普通)方法,那是万万不可的,因为先到的怎么可能去调用还没有进来的方法呢?
代码示例
代码示例:
class TestDemo{
public int a;
public static int count;
public static void change() {
count = 100;
//a = 10; error 不可以访问非静态数据成员
}
}
public class Main{
public static void main(String[] args) {
TestDemo.change();//无需创建实例对象 就可以调用
System.out.println(TestDemo.count);
}
}
执行结果:100
使用场景
- 大部分情况下一般使用成员属性和成员方法
- 当有一样东西是所有的对象需要共用的时候使用静态
三、类加载
定义
当虚拟机JVM第一次使用一个类的时候,它需要将这个类对应的字节码文件读入JVm,从而获取这个类的全部信息(包括类的父类、拥有的属性、方法、构造方法等),并保存起来(存在JVM的方法区)
在JVM运行的过程中,一个类只会加载一次,但对象创建的过程会被执行多次
什么时候会类加载?
1)执行main方法时,main方法所在的类被加载
2)第一次创建该类的对象(创建对象时,对象使用的类被加载)
3)通过类名调用类中的静态属性或静态方法(第一次访问类的静态成员)
4)加载子类时,也可能先加载父类形如Class.ForName(“类名”);
特别的,如果仅仅是声明类的引用就不需要类加载,例如 A a =null;
类加载的过程?
1.如有必要,先加载父类
2.为类的静态属性分配空间,分配默认值
3.按照代码顺序,初始化静态属性,或是运行静态初始代码块
四、final关键字
规则
- 类
该类不能被继承,不能再拥有子类(String字符串类就是被final修饰的类,因此它无法被继承。) - 方法
该方法不能被子类覆盖/重写(可以继承,子类不需写方法就行) - 变量
成员变量+局部变量(一旦赋值,就不能改变)
当final修饰成员变量的时候,系统不会提供默认值,就必须在初始化属性时或者在构造方法中赋值一次
修饰符组合
private、static、final 都不能和抽象类abstract同时出现