静态 (实现属性方法共享)
抽象(一个类可能要继承多个父类)
内部类(类的内部还有类)
静态属性
静态属性是属于类的,可以直接使用类名来访问,也可以使用对象访问
非静态属性,是属于对象的,一定要使用对象来访问,没有其他方式!
静态属性,是属于类的,并且是这个类所有对象共享的
静态属性的存储位置:
类中的静态属性,跟随着类,一起保存在内存中的方法区。
当创建对象的时候,对象中只会保存类中定义的非静态属性的信息,而静态属性是不会进入到对象中的。
静态属性的初始化:
无论是静态属性还是非静态属性,都必须进行初始化后才能使用,要么是系统给属性初始化赋默认值,
要么是我们自己手动给属性赋值。
属性的初始化时间:
非静态属性:创建对象后,系统会自动给对象中的非静态属性做初始化赋默认值,也正是因为这个原因,非静态属性只有在创建对象后,使用对象才能方法
静态属性:类加载到内存中(方法区)的时候,系统就会给类中的 静态属性做初始化赋默认值,所以,即使还没有创建对象,只要这个类加载到了内存,就可以直接使用类名来访问静态属性,因为这个时候静态属性已经完成了初始化赋默认值的操作。
静态方法可以被继承,但是不能被重写(但是可以写这个名字的方法(名字参数可以一样))
静态方法中,不能访问this
静态代码块,也叫做静态初始化代码块,它的作用就是给类中的静态属性做初始化的
静态代码块的执行时刻:
由于静态代码块没有名字,我们并不能主动调用,它会在类加载的时候,自动执行。
所以静态代码块,可以更早的给类中的静态属性,进行初始化赋值操作。
并且,静态代码块只会自动被执行一次,因为JVM在一次运行中,对一个类只会加载一次!
public class Demo {
public static int num;
static{
num = 10;
}
}
public static void main(String[] args){
System.out.println(Demo.num);//输出结果为 10
}
匿名代码块:
和静态代码块类似的,还有一种非静态代码块,也叫做匿名代码块,它的作用是给非静态属性做初始化操作
匿名代码块执行的时刻: 由于匿名代码块没有名字,我们并不能主动调用,它会在创建对象的时候,构造器执行之前,自动执行。 并且每次创建对象之前,匿名代码块都会被自动执行。
public class Demo {
public int num;
{
num = 10;
}
}
public static void main(String[] args){
Demo demo = new Demo();
System.out.println(demo.num);//输出结果为 10
}
public class Demo {
static {
System.out.println("静态代码块执行");
}
{
System.out.println("匿名代码块执行");
}
public Demo(){
System.out.println("构造器执行");
}
}
public static void main(String[] args){
new Demo();
new Demo();
}
//输出结果为:
静态代码块执行
匿名代码块执行
构造器执行
创建和初始化对象的过程:
Student s = new Student();
以这句代码为例进行说明:
对Student类进行类加载,同时初始化类中静态的属性赋默认值,给静态方法分配内存空间
执行类中的静态代码块
堆区中分配对象的内存空间,同时初始化对象中的非静态的属性赋默认值
调用Student的父类构造器
对Student中的属性进行显示赋值,例如 public int age = 20;
执行匿名代码块
执行构造器代码
=号赋值操作,把对象的内存地址赋给变量s
public class Person{
private String name = "zs";
public Person() {
System.out.println("Person构造器");
print();
}
public void print(){
System.out.println("Person print方法: name = "+name);
}
}
class Student extends Person{
private String name = "tom";
{
System.out.println("Student匿名代码块");
}
static{
System.out.println("Student静态代码块");
}
public Student(){
System.out.println("Student构造器");
}
public void print(){
System.out.println("student print方法: name = "+name);
}
public static void main(String[] args) {
new Student();
}
}
结果
Student静态代码块
Person构造器
student print方法: name = null
Student匿名代码块
Student构造器
注意1,子类重写父类的方法,在创建子类对象的过程中,默认调用的一定是子类中重写后的方法
注意2,非静态属性的显示赋值,是在 父类构造器执行结束之后 和 子类中的匿名代码块执行之前的时候
注意3,以上代码中,因为方法的重写,会调用子类中重写后的print方法,同时该方法恰好是在父类构造器执行中调用的,而这个时候子类中的name属性还没有进行显示赋值,所以是输出结果为null
注意4,如果此时在子类的匿名代码块中也输出name的值,那么就会显示tom,因为已经完成了属性的显示赋值
静态导入
import 。。。。。
final
用final修饰的类不能被继承,也就是说这个类是没有子类的。
用final修饰的方法可以被子类继承,但是不能被子类重写。
用final修饰的变量就变成了常量,并且它只能被赋一次值,第二次赋值就会报错
用static声明的final变量不会默认初始化,所以必须自己赋值,否则会报错(可在静态代码块里赋值,非静态的可在匿名块赋值)(static默认初始化后会赋初值,会导致后面的赋值没有意义)
常量命名一般全部大写
抽象
abstract
abstract修饰符,可以修饰类、方法
如果abstract修饰方法,那么该方法就是抽象方法。
抽象方法的特点:
只有方法的声明 没有方法的实现
如果abstract修饰类,那么该类就是抽象类。
抽象类和非抽象类的区别:
抽象类使用了abstract修饰符,而非抽象类没有使用
抽象类中可以编写抽象方法,而非抽象类中不能编写抽象方法
抽象类不能进行实例化创建对象,而非抽象类可以实例化创建对象
抽象类是用来被子类继承的,子类继承抽象类,并且实现抽象类中的抽象方法。
所以,当前我们遇到一个抽象类的时候,这个类肯定需要被继承,然后实现里面的抽象方法,或者重写里面的普通方法。
注意,实现父类中的抽象方法 和 重写父类中的普通方法,只是说法不同,但是它们的语法要求,操作方式完全是一样的,可以直接把实现抽象方法当做重写方法来看。
抽象类不实例化创建对象,那么抽象类中是否有构造器?
有构造器,这个构造器是让子类调用的,子类继承父类,子类创建对象的时候,会先调用父类的构造器
子类继承抽象父类,子类可以选择实现父类中所有的抽象方法,如果有任何一个抽象方法没有被子类实现,那么这个子类也要将自己声明为抽象类,那么这个子类也就只能等待,再来一个子类继承自己,去实现剩余没有实现的抽象方法,直到所有抽象方法都被实现为止。
抽象类可以没有抽象方法(构造器)但是定义了抽象方法的类必须是抽象类
接口interface (定义出来的目的就是为了被实现)
引用数据类型:类、数组、接口
接口是除了类和数组之外,另外一种引用数据类型
接口和类不同,类的内部封装了成员变量、构造方法和成员方法,而接口的内部主要就是封装了方法和静态常量。
接口的定义和类很类似,但是接口需要使用 interface 关键字来定义
接口最终也会被编译成.class文件,但一定要明确接口并不是类,而是另外一种引用数据类型
//使用interface关键字来定义接口
public interface Action {//特殊的父类
//接口中的静态常量
public static final String OPS_MODE = "auto";
//接口中的抽象方法
public void start();
//接口的抽象方法
public void stop();
}
注意1, 定义类使用关键字 class ,定义接口使用关键字 interface
注意2,接口中的属性都是公共的静态常量,且常量的名字一般需要全大写
注意3,接口中的方法都是抽象方法
注意:JDK8中,还允许在接口中编写静态方法和默认方法
注意:JDK9中,还允许在接口中编写私有方法
注意4,接口的中抽象方法,不需要使用 abstract 修饰符,因为接口中的方法默认就是抽象方法
接口里面的属性,默认就是public static final修饰的 (常量)
接口里面的方法,默认就是public abstract修饰的
所以这些都可以省去不写的
接口中没有构造器
接口实现:
类和类之间的关系是继承,类和接口之间的关系是实现:一个类实现了一个或多个接口(特殊的继承)
接口和类不同,类可以实例化创建对象,而接口不能创建对象,只能让其他类来实现。
一个类实现了一个接口,那么该类可以称为这个接口的实现类。类实现一个接口 和 类继承一个父类 的效果类似,格式相仿,只是关键字不同,实现使用 implements 关键字,而继承使用的是extends关键
字。
一个类实现了接口,那么就要实现接口中所有的抽象方法,否则这个类自己就必须声明为抽象类。
public interface Action {
void run();
void sayHello();//接口中的普通方法都是抽象方法
}
//类实现接口,没有实现接口中的抽象方法,那么这个类就必须声明为抽象类
abstract class Student1 implements Action{
}
//类实现接口,并且实现了接口中所有的抽象方法
class Student2 implements Action{
public void run(){
//...
}
public void sayHello(){
//...
}
}
一个类可以实现多个接口,实现了多个接口,那么就需要把这多个接口中的抽象想法全都实现
public interface Action {
void run();
void sayHello();
}
public interface Mark{
void star();
}
public interface Action {
void run();
void sayHello();
}
public interface Mark{
void star();
}
所以java被称为单继承多实现
接口之间也可以继承,并且可以多继承
//实现该接口的类,将具有run的功能
public interface Runable {
void run();
}
//实现该接口的类,将具有fly的功能
interface Flyable{
void fly();
}
//实现该接口的类,将具有run的功能,fly的功能,以及Action接口独有的doSomething功能
interface Action extends Runable,Flyable{
void doSomething();
}
//实现类,实现Action接口,就必须要实现Action及其父接口中的所有抽象方法
class Demo implements Action{
@Override
public void run() {
}
@Override
public void fly() {
}
@Override
public void doSomething() {
}
}
如何让开发人员,一定能按照我们的预先设计好的方法,去编写代码?
面向接口编程:
访问控制
修饰符:public > protected > default > private
单例(设计模式):
不管任何时候,通过这个类获取对象时,永远拿到的是同一个对象
class A{
//创建A
private static A instance= new A();
//public A(){}
private A(){}
//提供方法获取创建的对象
public static A tt(){
return instance;
}
}
class B{
}
public class Main {
public static void main(String[] args) {
A a1 = A.tt();
A a2 = A.tt();
B b1 = new B();
B b2 = new B();
System.out.println(a1.equals(a2));
System.out.println(b1.equals(b2));
}
}
true
false