每个人都可以活得更精彩,不要让惰性毁了你,坚持学习。
随着我们对面向对象的不断了解,我们已经学过很多语法和思想了,虽然不是很好理解,但是也没有难到令人放弃,随着不断地练习,总有一天自己会瞬间通透的;更难学的还在后面。不要觉得Java很难,多给自己点信心,加油。
那好,聊到这里我们继续学习更难的知识点。
1. 类变量和类方法(静态变量)
类变量(静态变量)
引出: 假设有一群小孩在堆雪人,不断地有小孩要加入;问;某个时刻共有多少个小孩?
思路: main中定义count,每个小孩加入后count++。
问题分析: count是个独立对象,访问起来很麻烦,并且没有用到面向对象(OOP)
问题解决: 在类中引入一个类变量(静态变量)static修饰 ,该变量最大的特点就是被该程序中的所有对象共有。
例如:
public class demo { public static void main(String[] args) { new Child("小明"); new Child("小红"); System.out.println(Child.count); } } class Child { private String name; public static int count = 0; public Child(String name) { this.name = name; count++; } }
图示:
内存刨析:
随着版本的不同,静态变量存放的位置不同。
有的书是这样得:
而有的书是说,static得信息存在静态域中:
无论放在哪里,对我们的使用并不影响。
注意事项和使用细节:
🐤 何时使用:需要所有对象共享一个变量
🐤 类变量和实例变量(普通变量)的区别:
类变量是该类所有对象所共享对的,而实例变量是个类对象独有的。
🐤 加上static成为类变量或静态变量,否则称之为实例对象、非静态变量或普通变量。
🐤 类变量的访问方式:
1. 在同一个类中访问:直接引用变量名即可
2. 在不同类中访问:必须通过类名.类变量名字;如:Child.count
原因:类变量是随着类的加载而加载的,所以在类的加载时就可以访问到类变量信息。
🐤 类变量的周期从类的开始到类的结束。
类方法(静态方法):
语法形式:
访问修饰符 static 返回类型 方法名() { } (推荐) 或者
static 访问修饰符 返回类型 方法名() { } (不推荐)
调用:
语法:类名.方法名 或者 对象名.方法名 (前提是满足访问修饰符的访问权限和范围)
注意:
非静态的可以调用静态的,但静态的只能调用静态的不可以调用非静态的。
在程序运行时,类先加载,静态此时也就加载,在对象创建后才有了非静态;所以时先有静态后有非静态。
使用场景:
当方法不涉及到任何和对象相关的成员时,则可以将其设为静态方法,以便提升效率。
比如:工具类中的Utils、Math工具、Array类。
注意事项和细节讨论:
🐤 类方法和普通方法都是随着类的加载而加载的,将结构信息储存在方法区:
类方法中无this的参数
而普通方法隐藏着this的参数
🐤类方法可以通过类名调用,也可以通过方法名调用
🐤普通方法和对象有关,需要通过对象名调用,比如:对象名.方法名(参数)【dog.eat()】 不能通过类名调用
例如:
class Dog { public void say() { //非静态 } public static void hi(){ //静态 } } public class demo { public static void main(String[] args) { Dog.hi(); Dog.say();//报错 } }
🐤 类方法中不允许使用与对象有关的关键字,例如:this和super
🐤 类方法只能访问静态变量或静态方法,而普通成员中既可以访问静态也可以访问非静态
总结:静态可以访问静态的,非静态可以访问静态和非静态的。
2. main方法中的语法
深入理解main方法
1. mian方法被Java虚拟机调用
2. Java虚拟机调用main方法访问权限必须是public
3. Java虚拟机在执行main方法时不需要创建对象,所以i方法必须是static的
4.该方法接受String类型的数组传参,该数组中保存执行Java命令时传递所给类的参数:
也就是我们main方法中的这个:
String[] args 值是由执行程序时传进去的参数形成的一个数组。
例如:
public class Main { private static String name = "小明"; private int age = 10; public static void main(String[] args) { System.out.println(name); hi(); System.out.println(age);//错位,main中可以访问静态的,但是不可以访问非静态的 } public static void hi() { System.out.println("hello"); } }
提示:
🐔: 在main方法中,我们可以直接调用main方法所在类的静态方法或静态属性
🐔: 但是,不能直接调用非静态的成员变量;想要调用,只能通过关键字 new 出实例对象,通过对象去访问。
如何在idea上实现main的动态传值:
由于我的是社区版的我就把b站老韩的视频链接放下来:
0384_韩顺平Java_main动态传值_哔哩哔哩_bilibili
https://www.bilibili.com/video/BV1fh411y7R8?p=385&vd_source=93857e709631270a9e771d5aaee79ecb
3. 代码块
基本介绍:代码块又称初始化块,属于类中的成员【即类的一部分】,类似于方法,讲语法封装在方法体中,用 { } 包围起来。
但和方法不同,无名,无参,无返回值,只是在类加载时,或创建对象时隐式调用。
语法:
修饰符{
代码
};//“ ;”可写可不写
根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块
构造块
静态块
同步代码块(后续讲解多线程部分再谈)
🐔 普通代码块
此时的修饰符就是default默认不写;
{
代码
}
🐔 静态代码块
此时的修饰符就是static
static{
代码
}
🐔 构造块
定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量
使用场景:
如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的复用性。
举例:🌰
public class Student{ private String name; private String gender; private int age; private double score; private static String classRoom; //实例代码块 { this.name = "marry"; this.age = 12; this.gender = "man"; System.out.println(name+" is instance init()!"); } // 静态代码块 static { classRoom = "303"; System.out.println("I am static init()!"); } // 构造代码块 public Student(){ System.out.println("I am Student init()!"); } public static void main(String[] args) { Student s1 = new Student(); Student s2 = new Student(); } }
从这个例子我们可以看出如下总结:
🐔创建对象时,会有如下顺序:
执行静态代码块 ——> 执行普通代码块 ——> 执行构造代码块
🐔 静态代码块仅执行一次,静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
🐔如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
🐔实例代码块只有在创建对象时才会执行
如果我们在此基础上加上继承,结构又会如何,执行顺序又会如何?
public class demo { public static void main(String[] args) { new Son(); } } class Father { public Father() { System.out.println("我是父类的构造代码块"); } { System.out.println("我是父类的代码块"); } static{ System.out.println("我是父类静态代码块"); } } class Son extends Father{ public Son() { System.out.println("我是子类的构造代码块"); } { System.out.println("我是子类的代码块"); } static{ System.out.println("我是子类静态代码块"); } }
可以发现如下顺序:
执行父类静态代码块 ——> 执行子类静态代码块 ——> 执行父类普通代码块 ——> 执行父类构造代码块——> 执行普通代码块 ——> 执行构造代码块