类变量和类方法
static变量是被所有对象共享的;static变量在类加载的时候就生成了
定义的格式:
访问修饰符 static 类型 变量名 = 值;
访问方式:
类名.变量名或者对象名.变量名
类变量的使用细节
- 类变量与普通变量的区别,类变量是所有对象共享的,普通变量是对象独享的
- 类变量可以通过类名.类变量名或者对象.类变量名访问
- 类变量在类加载的时候就初始化了,即使没有创建对象也能使用类变量
- 类变量的生命周期随着类的加载而创建,随着类的销毁而销毁
类方法也叫静态方法,比普通方法多一个static修饰,访问方式可以通过类名.方法名或者对象.方法名访问。
类方法的使用细节
- 类方法和普通方法都是随着类的加载而加载,将信息存储在方法区,类方法中没有this,普通方法中有this
- 类方法可以通过类名.方法名或者对象.方法名调用,普通方法只能通过对象.方法名调用
- 类方法中不能使用this、super关键词
- 类方法只能访问类变量和其它类方法,不能访问非类变量和普通方法
- 普通方法可以访问类变量和普通变量,也可以访问其它普通方法和类方法
深入理解mian方法
格式:public static void main(String[] args)
- main方法是Java虚拟机调用的,必须为public,当不用public修饰的时候我们是不能调用main方法,不在同一个类中。
- 使用static修饰的时候,Java虚拟机不用创建类就能调用对象
- String[] 参数用于接受传入的参数,可以是多个,我们在执行程序的时候传入,java 程序 参数1 参数2 参数3
代码块
代码块又称为初始化块,属于类的成员,类似于方法,但是与方法又不同,没有方法名,没有返回值,访问修饰符只能使用static修饰,不能通过对象显示调用,只能在类加载的时候或者创建对象的时候隐式调用。
语法格式:
[访问修饰符static]{
}
代码块分为:静态代码块或者普通代码块
当构造器有相同的语句的时候,我们可以抽象出来,写在代码块中。
代码块的使用细节
-
静态代码块在类加载的时候,对类进行初始化,只会加载一次,普通代码块对对象进行初始化,在创建对象的时候进行调用,每创建一个对象就会调用一次。
-
类什么时候被加载:
①创建类的实例的时候(new)②创建子类实例的时候,父类会被加载
③使用类的静态属性或者静态方法的时候,类会被加载
-
普通代码块,在创建对象实例的时候会被加载,创建一次对象,就会加载一次对象,如果只是使用类的静态成员,普通代码块是不会被加载的,普通代码块相当于构造器的补充。
-
创建一个对象的时候,在一个类的调用顺序是:(重点)
①调用静态代码块和静态属性初始化(静态代码块、静态属性的优先级是一样的,如果有多个静态代码块和静态变量初始化,按照他们定义的顺序调用)
class Animal{ static{ System.out.println("静态代码块被调用"); } private static int num = getNum(); public static int getNum(){ System.out.println("静态方法getNum被调用"); return 100; } } Animal animal = new Animal(); //静态代码块被调用 //静态方法getNum被调用
②普通代码块和普通属性的初始化,普通代码块和普通属性的优先级是一样,如果定义多个普通代码块和普通属性,按照他们定义的顺序进行调用初始化
class Animal{ static{ System.out.println("静态代码块被调用");//1 } private static int num = getNum(); private int num2 = getN2(); { System.out.println("普通代码块1调用..");//4 } public static int getNum(){ System.out.println("静态方法getNum被调用");//2 return 100; } public int getN2(){ System.out.println("普通方法N2被调用");//3 return 200; } } Animal animal = new Animal(); /* 静态代码块被调用 静态方法getNum被调用 普通方法N2被调用 普通代码块1调用.. */
③构造器调用
class Animal{ static{ System.out.println("静态代码块被调用");//1 } private static int num = getNum(); private int num2 = getN2(); { System.out.println("普通代码块1调用..");//4 } public static int getNum(){ System.out.println("静态方法getNum被调用");//2 return 100; } public int getN2(){ System.out.println("普通方法N2被调用");//3 return 200; } public Animal(){ System.out.println("Animal无参构造函数进行初始化"); } } Animal animal = new Animal(); /* 静态代码块被调用 静态方法getNum被调用 普通方法N2被调用 普通代码块1调用.. Animal无参构造函数进行初始化 */
-
构造器的最前面其实隐含了super()和调用普通代码块,静态代码块和属性,在类加载的时候就完成了,因此优先与普通代码块和构造器的执行
class AAA{ { System.out.println("AAA的普通代码块"); } public AAA(){ //隐含两个语句 //1.super() //super() //2.调用普通代码块 System.out.println("AAA的无参构造函数"); } } class BBB extends AAA{ { System.out.println("BBB的普通代码块"); } public BBB(){ //隐含两个语句 //1.super() //super() //2.调用普通代码块 System.out.println("BBB的无参构造函数"); } } new BBB(); /* AAA的普通代码块 AAA的无参构造函数 BBB的普通代码块 BBB的无参构造函数 */
-
创建一个具有继承关系的子类对象时,他们的静态代码块、普通代码块、构造器执行顺序:
①先加载类信息,父类的静态代码块、静态属性执行,按照定义的顺序执行
②加载子类的信息,子类的静态代码块、静态属性执行,按照定义的顺序执行
③父类的普通代码块和普通属性进行初始化,按照定义的顺序执行
④父类的构造器执行
⑤子类的普通代码块和普通属性执行,按照定义的顺序执行
⑥子类的构造器执行
class A1{ private static int num1 = getN1(); static{ System.out.println("A1的静态代码块");//2 } private int num2 = getN2(); { System.out.println("A1的普通代码块");//6 } private static int getN1(){ System.out.println("A1的静态方法getN1进行静态变量的初始化");//1 return 100; } private int getN2(){ System.out.println("A1的普通方法getN2进行普通变量初始化");//5 return 200; } public A1(){ //隐含了两个语句 //super() //普通代码块和普通变量的初始化 System.out.println("A1的无参构造函数");//7 } } class B2 extends A1{ private static int num1 = getN1(); static{ System.out.println("B2的静态代码块");//4 } private int num2 = getN2(); { System.out.println("B2的普通代码块");//9 } private int getN2(){ System.out.println("B2的getN2执行,进行普通变量的初始化");//8 return 100; } public static int getN1(){ System.out.println("B2的静态方法执行,静态变量被初始化");//3 return 200; } public B2(){ //隐含了两个语句 //super() //普通代码块和普通变量的初始化 System.out.println("B2的无参构造函数被执行");//10 } } new B2(); /* A1的静态方法getN1进行静态变量的初始化 A1的静态代码块 B2的静态方法执行,静态变量被初始化 B2的静态代码块 A1的普通方法getN2进行普通变量初始化 A1的普通代码块 A1的无参构造函数 B2的getN2执行,进行普通变量的初始化 B2的普通代码块 B2的无参构造函数被执行 */
-
静态代码块只能调用静态成员,普通代码块可以调用任意成员
单例设计模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
饿汉式:
1.构造器私有化
2.内部创建对象实例
3.对外提供一个公共方法访问这个实例
public class Singleton {
private Singleton(){
}
private static Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
懒汉式:
1.构造器私有化
2.内部创建对象引用,不进行初始化,在调用对象的时候进行初始化
3.对外提供一个公共方法访问这个实例
public class Singleton {
private Singleton(){
}
private static Singleton singleton;
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
饿汉式和懒汉式区别:
1.饿汉式在加载类信息的时候就创建了类的实例,不存在线程安全问题,但是不能实现懒加载的效果
2.懒汉式在需要使用类的实例的时候才创建类实例,实现了懒加载,但是只能在单线程的情况下使用,存在线程安全问题
final关键词
final可以修饰类、属性、方法、局部变量。
当我们不需要某个类被继承的时候可以使用final修饰;
当我们不需要某个方法被子类重写的时候可以使用final修饰
当我们希望某个值被赋值之后再修改(常量)可以使用final修饰
final细节
-
final修饰属性的时候,在定义的时候必须赋值,赋值可以在下列位置之一:
①定义的时候
②构造器中
③代码块中public class Demo3 { private final double NUM = 1.0;//在定义的时候赋值 private final double NUM2; private final double NUM3; public Demo3(){ NUM2 = 2.0;//在构造器中赋值 } { NUM3 = 3.0;//在代码块中赋值 } }
-
如果final修饰的属性是静态的,那么赋值只能在定义的时候或者静态代码块中赋值,不能在构造器中赋值
class CC{ private static final double NUM = 1.0; private static final double NUM1; static { NUM1 = 2.0; } }
-
final修饰的类是不能被继承的,但是可以被实例化,比如:String
-
final修饰的方法不能被重写,但是可以被继承
-
如果一个类已经是final类,就没有必须再将方法修饰成final
-
final不能修饰构造器
不同于方法,构造器不能是abstract, static, final的.
①构造器不是通过继承得到的,所以没有必要把它声明为final的。
②同理,一个抽象的构造器将永远不会被实现,所以它也不能声明为abstract的。
③构造器总是关联一个对象而被调用,所以把它声明为static是没有意义的。
-
final和static搭配使用,效率会更高,不会导致类的加载,底层会进行优化
//优化前 public class Demo4 { public static void main(String[] args) { System.out.println(DD.NAME); } } class DD{ public static String NAME = "NAME"; static{ System.out.println("静态代码块被执行"); } } /* 静态代码块被执行 NAME */ //优化后: public class Demo4 { public static void main(String[] args) { System.out.println(DD.NAME); } } class DD{ public final static String NAME = "NAME"; static{ System.out.println("静态代码块被执行"); } } /* NAME */
-
包装类和String都是final修饰的类
抽象类
当父类的某个方法不确定如何实现的时候,可以将其声明为抽象方法,将其交给子类去实现,这样父类就变成了抽象类,当一个类有一个抽象方法的时候,就必须将其声明为抽象类,抽象类不能实例化。
abstract class Animal{
private String name;
public void setName(String name){
this.name = name;
}
public abstract void eat();
}
抽象类的价值更多在于设计,让子类继承并实现,在框架和设计模式中使用较多
抽象类细节
-
抽象类是不能被实例化的
-
抽象类不一定包含抽象方法,抽象类还可以包含普通方法,构造函数,但是包含抽象方法的类一定是抽象类
-
修饰符abstract只能修饰类和方法,不能修饰属性
-
抽象类还是类,可以包含任意的成员
abstract class Animal{ //属性 private String name; public void setName(String name){ this.name = name; } //静态方法 public static void hi(){ } //构造器 public Animal(){ } //抽象方法 public abstract void eat(); }
-
抽象方法不能有主体
-
如果一个类继承了抽象类,则必须实现所有的抽象方法,除非自己也是抽象类
abstract class Animal{ public abstract void eat(); } class Cat extends Animal{ @Override public void eat() { } }
-
抽象方法不能使用private、final、static来修饰,因为这些关键词都与重写相违背
接口
接口就是说给一些没有实现的方法封装到一起,到某个类要使用的时候,再把具体的方法实现
语法格式:
interface 接口名{
抽象方法
属性
}
实现接口
class 类 implements 接口名{
}
注意:
在JDK7之前,接口中所有的方法都是抽象方法,在JDK8后,接口中可以有静态方法、默认方法,也就是说接口中的方法可以有实现体。
public interface AInterface {
public int num = 10;
//抽象方法
public void hi();
//默认方法
default public void hello(){
System.out.println("hello");
}
//静态方法
public static void cat(){
System.out.println("cat");
}
}
接口细节
-
接口是不能被实例化的
-
接口所有的方法都是public,接口中抽象方法可以不用abstract修饰
-
一个普通类实现接口,必须实现接口中的所有方法
-
抽象类实现接口,可以不用实现接口中的方法
-
一个类可以同时实现多个接口
interface A{ } interface B{ } class C implements A,B{ }
-
接口中的属性只能是final,而且是public final static修饰的,比如:int num = 1;实际上是:public final static int num = 1;访问形式是:接口名.属性名
-
接口不能继承其它类,但是能继承其它接口
-
接口只能使用public和默认的修饰符修饰,跟类一样
接口和继承类比较
- 解决的问题不同
继承类主要解决代码复用性和可维护性
接口主要解决的设计规范让子类去实现这些规范 - 接口比继承更加灵活
继承是is-a关系,接口是like-a关系 - 接口在一定程度是实现代码解耦
接口多态传递现象:
interface A{
}
interface B extends A{
}
class C implements B{
}
B b = new C();//正确
A a = new C();//正确
类的五大成员:成员属性、成员方法、构造器、代码块、内部类