⭐️静态变量被同一个类的所有对象共享!!
任何该类的对象访问的时候,取到的都是相同的值
任何该类的对象去修改的时候,修改的也都是同一个变量
⭐️静态变量在类加载的时候就生成了!!
- 在加载类的时候也初始化了静态变量,也就是说:即使没有创建类对象,只要类加载了,就可以使用静态变量
- 类变量的生命周期是随类的加载开始的,随类的消亡而销毁
一些小区别:jdk8以前的版本,static变量在方法区里。jdk8之后认为static变量通常在堆里:当一个类加载的时候,会在堆里生成一个对应这个类的class对象(属于一个原型对象),而静态变量就在这个class对象的尾部。
访问修饰符 static 数据类型 变量名
public static int num;
【推荐使用】: 类名.类变量名
或者: 对象名.类变量名

需要让某个类的所有对象共享一个变量的时候。
其与实例变量(普通属性)的区别是:类变量是该类所有的对象共享的,而实例变量是每个对象独有的,实例变量不能通过类名.类变量名访问。
注意:在Java中,子类不能继承父类的静态属性。静态属性(也称为类属性)是属于类的,而不是属于任何类的实例。因此,它们不会被子类继承。
但是,子类可以通过类名直接访问父类的静态属性,前提是这些属性是可访问的(例如,它们是公开的或受保护的)。
访问修饰符 static 数据返回类型 方法名(){}
private static double look(){}
【推荐使用】: 类名.类方法名
或者: 对象名.类方法名
如果不希望创建实例,也可以调用某个方法:类名 方法名()。也就是当方法中不涉及任何和对象相关的成员(即当做工具来使用时),就可以把方法设计成静态方法,提高开发效率。

public static void main(String[] args){}
publicstatic代码化块又称为初始化块,属于类中的成员(即是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来。
但和方法不同,代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是⭐️加载类时或创建对象时隐式调用。
相当于是另外一种形式的构造器(相当于是构造器的补充机制),去做初始化的操作,⭐️代码块调用的顺序优先于构造器。
如果多个构造器都有重复的语句,可以抽取到初始化块中,提高代码复用性。
[修饰符可有可无]{
代码
};
当多个构造器都有相同的语句 ,这样代码看起来比较冗余 , 这时我们可以把相同的语句,放入到一个代码块中。这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容 。代码块调用的顺序优先于构造器。
💁静态代码块 & 静态属性初始化:随着类的加载而执行,并且只会执行一次。
💁普通代码块 & 普通属性初始化:每创建一个该类的对象,就执行一次。
⭐️⭐️⭐️类什么时候被加载❓【重要!!!!】
创建对象实例的时候new;
创建子类对象实例父类也会被加载 ,且先加载父类后加载子类;
使用类的静态成员(静态变量,静态方法)时,该类也会被加载。
普通的代码块,在创建对象实例的时候,会被隐式的调用,对象被创建一次,就会调用一次;
如果只是使用类的静态成员,普通代码块并不会执行(因为使用类的静态成员并没有创建对象)
💥构造器的最前面其实是隐藏了super()和 调用本类的普通代码块以及普通属性初始化。
class A{}
public A(){//构造器
System.out.println("okok");
}
}
上面的构造器在实际运行时,实际上隐藏的一些执行要求是这样的⤵️
class A{}
public A(){//构造器
super(); // 1.默认调用父类的无参构造器
// 2.调用普通代码块 以及 普通属性初始化
// 3.最后再调用构造器内部的语句
System.out.println("okok");
}
}
举例说明

执行过程:新建一个BBB类的对象,首先进入BBB构造器,进入后:
(1)默认的super( )调用父类AAA的无参构造器
(2)默认的super( )调用父类Obgect的无参构造器 返回
(3)调用本类AAA的代码块,输出【这是AAA的普通代码块】
(4)执行AAA的构造器中的内容,输出【这是AAA的构造器】
(5)调用本类BBB的代码块,输出【这是BBB的普通代码块】
(6)执行BBB的构造器中的内容,输出【这是BBB的构造器】
⭐️⭐️⭐️创建一个对象时:在 一个类中 的调用顺序是:
先调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
再调用普通代码块和普通属性的初始化(注意: 普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
最后调用构造器方法
⭐️⭐️⭐️创建一个子类对象时:静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序是:
创建对象的时候首先就是要进行类的加载(先加载父类,后加载子类)
进行对象的创建(先从子类的构造器开始,子类构造器里默认包含super() 调用父类无参构造器 and 调用本类的普通代码块以及普通属性初始化,之后在进行构造器内容)
package com.hspedu.static_;
public class constructor {
public static void main(String[] args) {
new BBB();
// 先加载类 加载父类-加载子类
// 在执行创建对象的一些操作:子类构造器--super()父类构造器--普通代码块、普通属性初始化--构造器内容
}
}
class AAA{ // 父类
public int n = n();
public static int m = m();
static {
System.out.println("这是AAA的静态代码块");
}
{
System.out.println("这是AAA的普通代码块");
}
public AAA(){
// (1) super() 调用父类Object的构造器
// (2) 调用本类的普通代码块以及普通属性初始化
System.out.println("这是AAA的构造器");
}
public int n(){
System.out.println("这是AAA的普通属性初始化");
return 1;
}
public static int m(){
System.out.println("这是AAA的静态属性初始化");
return 0;
}
}
class BBB extends AAA{ //子类
static {
System.out.println("这是BBB的静态代码块");
}
{
System.out.println("这是BBB的普通代码块");
}
public BBB(){
// (1) super() 调用父类AAA的构造器
// (2) 调用本类的普通代码块以及普通属性初始化
System.out.println("这是BBB的构造器");
}
}
>>>输出
这是AAA的静态属性初始化
这是AAA的静态代码块
这是BBB的静态代码块
这是AAA的普通属性初始化
这是AAA的普通代码块
这是AAA的构造器
这是BBB的普通代码块
这是BBB的构造器
静态代码块只能调用静态成员(静态属性和静态方法)
普通代码块可以调用任意成员
静态属性和方法的继承
class Person {
public static int total;//静态变量
static {//静态代码块
total = 100;
System.out.println("in static block!");//(1) 只在加载类的时候执行一次!!一次!!
}
}
public class Test {
public static void main(String[] args) {
System.out.println("total = "+ Person.total); //100
System.out.println("total = "+ Person.total); //100
}
}
>>>输出
in static block!
100
100
单例——单个的实例
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
单例模式有两种方式: 1)饿汉式 2)懒汉式
演示饿汉式和懒汉式单例模式的实现步骤如下:
0391_韩顺平Java_单例模式饿汉式_哔哩哔哩_bilibili
package com.hspedu.static_.single;
public class SingleTon01 {
public static void main(String[] args) {
使用类名就可以调用getInstance静态public方法,返回创建的gf对象,
且由于创建对象时 gf是静态的,所以类加载的时候只执行一次,因此无论后面怎么调用,返回的gf对象都是同一个
GirlFriend instance1 = GirlFriend.getInstance();
System.out.println(instance1);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
// instance1 和 instance2是一个对象
System.out.println(instance1 == instance2);
}
}
// 有一个类 GirlFriend 只允许有一个女朋友!!!!
// 如何保证只能创建一个GirlFriend对象?【饿汉式】
// 1.构造器私有化 —— 不能new对象
// 2.在类的内部创建对象(该对象是static)
// 3.提供一个public的static方法,返回gf对象
class GirlFriend{
private String name;
// 【在类的内部创建一个对象】
// 同时,为了保证静态方法getInstance可以使用 gf对象,所以需要修饰新建的gf对象为static
private static GirlFriend gf = new GirlFriend("小红");
// 【构造器私有化】 保证不能在外面肆意new对象
private GirlFriend(String name) {
this.name = name;
}
// 【提供一个public的static方法,返回gf对象】
// static用来保证 不用新建一个GirlFriend对象就可以调用这个方法
public static GirlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
>>> GirlFriend{name='小红'}
GirlFriend{name='小红'}
true
0392_韩顺平Java_单例模式懒汉式_哔哩哔哩_bilibili
package com.hspedu.static_.single;
public class SingleTon02 {
public static void main(String[] args) {
Cat mycat1 = Cat.getInstance();
System.out.println(mycat1);
Cat mycat2 = Cat.getInstance();
System.out.println(mycat2);
// mycat1 和 mycat2 是一个对象
System.out.println(mycat1 == mycat2);
}
}
// 希望程序进行中, 只能创建一个Cat对象!!!!!
// 使用单例模式【懒汉式】
// 1.仍然构造器私有化
// 2.定义一个static属性对象,但是不初始化
// 3.提供一个public的static方法 可以返回一个Cat对象
// 4.懒汉式:只有当用户使用getInstance方法的时候,才返回Cat对象,后面再次调用时会返回上次创建的cat对象,从而保证了单例
class Cat{
private String name;
// 【定义一个static属性对象 但是不初始化,没有new创建一个对象的话构造器是不会被调用的】
private static Cat cat;
// 【构造器私有化】
private Cat(String name){
this.name = name;
}
// 【提供一个public的static方法 可以返回一个Cat对象】
// 只有当用户使用getInstance方法的时候,才返回Cat对象。如果不是调用getInstance方法,那么虽然可能类被加载,但是没有创建对象
// 后面再次调用时会返回上次创建的cat对象,从而保证了单例模式
public static Cat getInstance(){
if(cat == null){ // 如果没有创建cat对象,那么就创建一个cat对象
cat = new Cat("小花猫");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
>>>Cat{name='小花猫'}
Cat{name='小花猫'}
true

Runtime是饿汉式的:在类加载就创建对象实例,可能存在资源浪费的问题
final 可以修饰类、属性、方法和局部变量在某些情况下,程序员可能有以下需求,就会使用到final:
当不希望类被继承时,可以用final修饰.【final class】
当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【访问修饰符 final 返回类型 方法名】
当不希望类的的某个属性的值被修改,可以用final修饰。【public final double RATE=0.08】
当不希望某个局部变量被修改,可以使用final修饰【final double RATE=0.08】
final修饰的属性又叫常量,一般用XX_XX_XX来命名(大写)
final修饰的属性在定义时必须赋初值,并且以后不能再修改!
赋值可以在如下位置之一 [ 选择一个位置赋初值即可 ]
在定义时: 如 public final double TAX_RATE=0.08
在构造器中赋值
在代码块中赋值
private final double PI = 3.14; // 赋值位置1 :定义时候就赋值
private final double PI; // 赋值位置2 : 构造器内
public Circle(double radius){
this.radius = radius;
PI = 3.14;
}
private final double PI; //赋值位置3:代码块中赋值
{
PI = 3.14;
}
如果final修饰的属性是静态的,则初始化的位置只能是:
final类不能继承,但是可以实例化对象。
如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
一般来说如果已经是一个final类了,就没必要修饰final方法了[都不能被继承了 那重写不重写方法没意义]
final不能修饰构造器。
final和static搭配使用,不会导致类的加载,效率更高。

包装类(Integer Double Float Boolean String等)都是final类,都不能被继承
final x,++改变了x的值所以不行,x+1没有改变本身x的值所以可以

当父类的某些方法需要声明,但是又不确定如何实现的时候
=》可以将其声明为抽象方法——用abstract关键字来修饰该方法,对应的这个类就是抽象类——用abstract修饰该类。
package com.hspedu.abstract_;
public class Abstract01 {
public static void main(String[] args) {
}
}
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//思考:这里 eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 【考虑将该方法设计为抽象(abstract)方法】
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 【当一个类中存在抽象方法时,需要将该类声明为 abstract 类】
//===> 一般来说,抽象类会被继承,由其子类来实现抽象方法.
public abstract void eat() ;
}
class cat extends Animal{
public void eat(){
//如果一个类继承了抽象类,则他必须实现抽象类的所有抽象方法,否则这个类也必须声明为抽象类
}
}
需求:有多个类完成不同的任务job,要求统计得到各自完成任务的时间
模板设计模式能解决的问题:
当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式
设计一个抽象类(Template),能完成如下功能:
calculateTime(),可以计算某段代码的耗时时间jobSub,继承抽象类Template,并实现job方法TestTemplate,看看是否好用package com.hspedu.static_.abstract_.TestTemplate;
public abstract class Template { // 抽象类-模板设计模式
public abstract void job(); // 抽象方法
public void calculateTime(){ // 实现方法 调用了job方法
long start = System.currentTimeMillis();
job(); // 动态绑定机制,调用对象方法的时候,看运行类型,和对象绑定。
// sub1.calculateTime(),对象sub1的运行类型是Sub1,调用calculateTime方法,子类Sub1没有,找到父类Template类调用calculateTime方法,运行到调用job方法时,根据动态绑定机制:调用的是sub1运行类型——即Sub1的job方法。
// sub2.calculateTime()就调用Sub2的job
long end = System.currentTimeMillis();
System.out.println("耗时间" + (end-start));
}
}
class Sub1 extends Template{
@Override
public void job() { // 实现Template类的抽象方法job
int num = 0;
for (int i = 0; i < 80000; i++) {
num += i;
}
}
}
class Sub2 extends Template{
@Override
public void job() { // 实现Template类的抽象方法job
int num = 0;
for (int i = 0; i < 8000000; i++) {
num += i;
}
}
}
package com.hspedu.static_.abstract_.TestTemplate;
public class Test {
public static void main(String[] args) {
Sub1 sub1 = new Sub1(); // 新建一个子类对象 编译类型和运行类型都是Sub1
sub1.calculateTime(); // 调用模板类的方法计算时间
Sub2 sub2 = new Sub2(); // 新建一个子类对象 编译类型和运行类型都是Sub2
sub2.calculateTime(); // 调用模板类的方法计算时间
}
}
⭐️动态绑定机制:当调用对象方法的时候,该方法会和该对象的内存地址(也就是运行类型)绑定
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来
interface 接口名{
//属性
//可以有3种方法(1.抽象方法(不需要abstract关键字) 2.默认实现方法(default) 3.静态方法(static))
}
class 类名 implements 接口{
自己属性;
自己方法;
必须实现的使用的接口的抽象方法;
}

// 定义接口
public interface Manager {
void connect(); // 接口中所有的方法必须是public方法(默认),以及默认是abstract的;
void close();
}
// A程序员实现接口:把接口中的方法完成
public class MySQLDB implements Manager{
@Override
public void connect() {
System.out.println("connect MYSQL");
}
@Override
public void close() {
System.out.println("close MYSQL");
}
}
// B程序员实现接口:把接口中的方法完成...
// 测试
public class test {
public static void main(String[] args) {
// 连接MYSQL
MySQLDB mySQLDB = new MySQLDB();
my.mytest(mySQLDB);
}
}
class my{
public static void mytest(Manager manager){ // 接入接口,通过接口来调用方法
manager.connect();
manager.close();
}
}
>>>输出:
connect MYSQL
close MYSQL
接口不能被实例化(因为接口本身是一个抽象的概念,希望的就是其他类实现它);
接口中所有的方法必须是public方法;
接口中抽象方法可以不用abstract修饰;
一个普通类实现(implements)接口,就必须把该接口的所有方法都实现。
public interface myinterface {
void look();
void eat();
}
// 普通类实现接口,就必须把该接口的所有方法都实现
class Tiger implements myinterface{
public void look() {
System.out.println("looking");
}
public void eat() {
System.out.println("eating");
}
}
如果用抽象类实现接口,那么就可以不用实现接口方法
public interface myinterface {
void look();
void eat();
}
// 如果用抽象类实现接口,那么就可以不用实现接口方法
abstract class Tiger implements myinterface{
}
一个类可以同时实现多个接口~~
interface myinterface1 { // 接口1
void look();
}
interface myinterface2 { // 接口2
void look();
}
// 一个类可以同时实现多个接口
class Tiger implements myinterface1,myinterface2{
public void look() {
System.out.println("looking");
}
public void eat() {
System.out.println("eating");
}
}
接口中的属性,只能是final的!且只能是public static final修饰符!!!!!!!!!
int a = 1; 相当于:public static final int a = 1;
接口中属性的访问形式:接口名.属性名
接口不能继承其他类,但是接口可以继承(extends)多个别的接口
接口和接口是:interface A extends B,C;
类和接口是:class A implement B;
接口的修饰符只能是 public 或者 默认 ,和类的修饰符一样
public interface myinterface{}
interface myinterface{}

都正确 都输出23。因为A里面的int a = 23 实际上等价于public static final int a = 23;
因为a变量是public的,所以b是对象实例访问a可以。
因为接口A中a变量为static ,所以可以用过类名A调用
在Java中,子类不能继承父类的静态属性。静态属性(也称为类属性)是属于类的,而不是属于任何类的实例。因此,它们不会被子类继承。但是,子类可以通过类名直接访问父类的静态属性,前提是这些属性是可访问的public。而B实现了A,因此B可以使用A里面的属性。
实现接口是对 Java 单继承机制的补充
接口比继承更加灵活,继承满足is-a的关系,接口组织需要满足like-a的关系
接口在一定程度上实现代码的解耦【即接口规范性 + 动态绑定机制】
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
wuKong.flying();
}
}
//猴子
class Monkey {
private String name;
public Monkey(String name) {
this.name = name;
}
public void climbing() {
System.out.println(name + " 会爬树...");
}
public String getName() {
return name;
}
}
//接口
interface Fishable {
void swimming();
}
interface Birdable {
void flying();
}
class LittleMonkey extends Monkey implements Fishable,Birdable {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");
}
@Override
public void flying() {
System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");
}
}
// 小结: 当子类继承了父类,就自动的拥有父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式扩展.
// 可以理解 实现接口 是 对 java 单继承机制的一种补充
packagecom.hspedu.interface_;
public class InterfacePolyParameter{
public static void main(String[] args){
//接口的多态体现
//接口类型的变量if01 可以指向 实现了IF接口类的对象实例
IF if01 = new Monster();
if01 = newCar();
//继承体现的多态
//父类类型的变量a 可以指向 继承AAA的子类的对象实例
AAA a = new BBB();
a = new CCC();
}
}
// 接口
interface IF{}
class Monster implements IF{} // 实现接口
class Car implements IF{} // 实现接口
// 类
class AAA{}
class BBB extends AAA{} // 继承父类
class CCC extends AAA{} // 继承父类
package com.hspedu.interface_.test3;
public class InterfacePolyArr {
public static void main(String[] args) {
// 多态数组: 定义一个接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone(); //Phone实现了接口 所以可以放到数组内
usbs[1] = new Carema();
/*
给Usb数组中,存放Phone和Carema对象,
Phone类还有一个特有的方法call()
请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone 特有方法call
*/
for (int i = 0; i < usbs.length; i++) { // 遍历Usb数组,调用work方法,调用Phone的特有方法
usbs[i].work(); //动态绑定
if(usbs[i] instanceof Phone){
((Phone) usbs[i]).call(); //向下转型
}
}
}
}
interface Usb{
void work();
}
class Phone implements Usb{ // Phone实现Usb接口
@Override
public void work() {
System.out.println("手机工作中");
}
public void call(){ //Phone类的特有方法
System.out.println("手机可以打电话");
}
}
class Carema implements Usb{// Carema实现Usb接口
@Override
public void work() {
System.out.println("相机工作中");
}
}
如果IG继承了IH接口,而Teacher类实现了IG接口 ---------> 那么,实际上就相当于Teacher类也实现了IH接口.
/**
*演示多态传递现象
*/
public class InterfacePolyPass{
public static void main(String[] args){
//☆接口类型的变量可以指向实现了该接口的类的对象实例(多态 向上转型类比)
IG ig = new Teacher();
//如果IG继承了IH接口,而Teacher类实现了IG接口
//那么,实际上就相当于Teacher类也实现了IH接口.
//这就是所谓的接口多态传递现象.
IH ih = new Teacher();
}
}
interface IH{
void hi();
}
interface IG extends IH{}
class Teacher implements IG{
// 这里要实现hi方法的原因就是,如果IG继承了IH接口,Teacher又实现了IG,那么就相当于Teacher也要实现IH,所以必须这样,不然会报错。
@Override
public void hi(){}
}
//☆访问接口的 x 就使用 A.x
//☆访问父类的 x 就使用 super.x
interface A{
int x=0;
} //想到等价public static final int x=0;
class B{
int x=1;
}//普通属性
class C extends B implements A{
public void pX(){
//System.out.println(x);//错误,原因不明确x
//可以明确的指定x
//☆访问接口的 x 就使用 A.x
//☆访问父类的 x 就使用 super.x
System.out.println(A.x + " " + super.x);
}
public static void main(String[] args) {
new C().pX();
}
}