一、类变量(静态变量)的引入
- 利用传统方法的缺陷引入类变量:
- 思路:
- 1). 在main方法中定义一个变量count
- 2). 当一个小孩加入游戏后count++。最后count就能统计有多少个小孩玩游戏了
- 思路:
public class ChildGame {
public static void main(String[] args) {
// 定义变量count统计有多少人参加游戏
int count=0;
Child child1 = new Child("老大");
child1.join();
count++;
Child child2 = new Child("老二");
child2.join();
count++;
Child child3 = new Child("老三");
child3.join();
count++;
System.out.println("count="+count);
}
}
class Child{
private String name;
public Child(String name) {
this.name = name;
}
public void join(){
System.out.println(name+" 加入了游戏");
}
}
- 问题分析:
- 1). count是一个独立于对象的变量,地位很尴尬,对象不能调用这个变量。
- 2). 后面要访问count很麻烦,因为没有使用到OOP
由此,我们就可以引出 类变量(也叫静态变量)
- 传统方法缺陷的改进
- 前面方法的缺陷主要是count不是属于所有对象的-》
- 思考:还是设计一个int count 表示总人数,还是count++来计算人数的增加,但是这个count要设计为所有对象共享的,这便是类变量与一般变量的不同的之一了。
代码改进:
public class ChildGame {
public static void main(String[] args) {
// 定义变量count统计有多少人参加游戏
// int count=0;
Child child1 = new Child("老大");
child1.join();
// count++;
child1.count++;
Child child2 = new Child("老二");
child2.join();
child2.count++;
Child child3 = new Child("老三");
child3.join();
child3.count++;
// 类变量可以通过类名来访问
System.out.println("count="+Child.count);
// 下面三个的count都是同一个count
System.out.println(child1.count);//3
System.out.println(child2.count);//3
System.out.println(child3.count);//3
}
}
class Child{
private String name;
// 定义一个静态变量count
public static int count=0;
// 该变量最大的特点就是能够被所有的Child对象实例使用
public Child(String name) {
this.name = name;
}
public void join(){
System.out.println(name+" 加入了游戏");
}
}
-
static变量共识:
- 1)static变量是同一个类所有对象共享
- 2)static类变量,在类加载的时候就生成了
-
基本介绍:
类变量也叫静态变量/静态属性
,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。 -
语法:
- 访问修饰符 static 数据类型 变量名;【推荐使用这个】
- static 访问修饰符 数据类型 变量名;
- 如何访问:
- 类名.类变量名
- 或者对象名.类变量名【静态变量的访问修饰符的访问权限和普通变量一样】
public class visitStatic {
public static void main(String[] args) {
// 类名.类变量名
// 类变量是随着类的加载而创建的,所以即使没有创建对象也可以访问
System.out.println(A.name);
// 通过对象名.类变量名
A a = new A();
System.out.println(a.name);
}
}
class A{
// 类变量
// 类变量的访问,必须遵守 相关的访问权限
public static String name;
}
一、1、 类变量的使用细节和注意事项
- 什么时候使用类变量
- 当我们需要让某个类的所有对象都共享一个变量的时候,就可以考虑使用类变量(静态变量)。
- 类变量和实例变量(普通属性)的区别:
- 类变量是该类所有对象共享的,而实例变量则是每个对象独享的;
- 加上static的变量就称为:类变量(静态变量),否则称为:实例变量(普通变量/非静态变量)
- 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐使用类名.类变量名的方式来访问
- 实例变量不能通过
类名.变量名
访问 - 类变量是
在加载时就初始化了
,也就是说,即使你没有创建对象,只是类加载了,就可以使用类变量了 - 类变量的声明周期是
随类的加载开始,到类的消亡而销毁的
二、类方法
- 基本介绍:类方法也叫
静态方法
- 语法:
- 访问修饰符 static 数据返回类型 方法名(){} 【推荐】
- static 访问修饰符 数据返回类型 方法名(){}
- 类方法的调用:
- 使用方式:类名.类方法名 【静态方法的访问修饰符的访问权限和普通方法一样】
public class static_Method {
public static void main(String[] args) {
// 创建两个学生对象,交学费
Stu tom = new Stu("tom");
//这里可以使用,但是有警告了,而且当写到tom.的时候,也没有提示出来了
tom.payFee(100);
Stu.payFee(100);
Stu.showFee();
//这样就可以不创建对象,直接使用方法计算两个数的和了
System.out.println(Cul.sum(8,199));
}
}
class Cul{
public static int sum(int a,int b){
return a+b;
}
}
class Stu{
private String name;
int a=10;
// 定义一个静态变量来累积学生的学费
private static double fee=0;
public Stu(String name) {
this.name = name;
}
// 1. 当使用static修饰方法后,该方法就变成了静态方法
public static void payFee(double fee){
Stu.fee+=fee;
}
public static void showFee(){
System.out.println("总学费有 :"+Stu.fee);
}
}
- ** 类方法的使用场景**
- 当方法中不涉及到任何对象相关的成员,则可以将方法设计成静态方法,提高开发效率
- 比如:工具类的方法: Math类,Arrays类,Collections集合类…
- 当方法中不涉及到任何对象相关的成员,则可以将方法设计成静态方法,提高开发效率
小结: 在程序员实际开发,往往会将一些通用的方法,设计为静态方法,这样我们不需要创建对象就可以使用了,比如打印一堆数组,冒泡排序,完成某个计算任务…
二、类方法使用的注意事项和细节:
1)类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区
2)普通方法和对象有关,需要通过对象名调用,不能通过类名调用
3)类方法中不允许使用对象有关的关键字,比如this,super。普通方法可以
4)类方法中只能访问静态变量或静态方法。
5)普通成员方法既能访问普通方法,也能访问静态方法
- 小结: 静态方法,只能访问静态的成员,非静态方法,可以访问静态成员和非静态成员【必须遵循访问权限】
三、main方法
- 解释main方法的形式:public static void main(String[] args){}
- main方法是由虚拟机调用的。因为java虚拟机需要调用类的mian()方法(java虚拟机跟main方法的类不是同一个类),所有该方法就必须是public
- java虚拟机在执行main方法的时候,不需要创建对象,所以该方法必须为static方法
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所允许的类的参数。
- 在命令行执行java程序的时候:java 执行的程序 参数1 参数2 参数3 …
- 这些参数就会成为main方法的参数数组args的变量,遍历输出args[i]就是输出这些添加进去的数,
- 如果没有添加任何数进去,那么就什么都不返回
– idea插入args数组的数据
点击这里之后-------------》弹出一个提示框,点击第一行(edit configurations…)
然后点击ok就可以了,这里可以自由地添加数据进去,用空格隔开就行。
public class Main_01 {
public static void main(String[] args) {
// args是如何传入的
// 遍历显示
for (int i=0;i<args.length;i++){
System.out.println("第"+(i+1)+"个参数为"+args[i]);
}
}
}
运行结果:
第1个参数为小学
第2个参数为大学
第3个参数为研究生
- 特别提示
- 在main方法中,我们可以直接调用mian方法所在类的静态方法或静态属性
- 但是,不能直接调用该类的非静态成员,必须创建该类的一个实例对象才能通过这个对象去访问类中的非静态成员
public class main_02 {
// 静态变量
private static String name="大刀爱java";
// 非静态属性
private int n1=1000;
// 静态方法
private static void hi(){};
// 非静态方法
public void cry(){}
public static void main(String[] args) {
// 1. 静态方法main,可以访问本类的静态成员
System.out.println("name="+name);
hi();
// 2. 静态方法main 不可以访问本类的非静态成员
// System.out.println(n1);//报错的
// cry(); //报错
// 3.main方法不能直接调用该类的非静态成员,
// 必须创建该类的一个实例对象才能通过这个对象去访问类中的非静态成员
main_02 main_02 = new main_02();
System.out.println(main_02.n1);
main_02.cry();
}
}
四、代码块
-
1基本介绍
- 代码化块又称为初始化块,属于类中的成员【即,是类中的一部分】,类似于方法,将逻辑语句封装在方法体中,通常用{ }包围起来
- 但和方法不同,没有方法名,没有返值,没有参数,只有方法体,而且不用通过对象或类显式调用,而且加载类的时候,或创建对象的时候隐式调用。
-
基本语法
修饰符{
代码(逻辑语句)
}
说明注意:
1)修饰符可选,要写的话,只能写static
2) 代码块分为两类:使用static修饰的是静态代码块;没有使用static修饰的叫普通代码块
3)逻辑语句可以为任何逻辑语句(输入,输出,方法调用,循环,判断...)
- 3代码块的好处
- 代码块相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 场景:如果多个构造器中都有重复的语句,就可以抽到初始化代码块中,提高代码块的重用性
public class block01 {
public static void main(String[] args) {
Movie movie1 = new Movie("你好");
System.out.println("==============================");
Movie movie2 = new Movie("唐人街探案", 100, "大刀");
}
}
class Movie{
private String name;
private double price;
private String director;
// 3个构造器--》
// 1)下面的三个构造器都有三条相同的语句
// 2)这样写显得代码很冗余
// 3)这时我们就可以将相同的代码块放进一个代码块中去
// 4)这样当我们不管调用哪个构造器创建对象,都会先调用代码块的内容
// 5)代码块调用的顺序优先于构造器
public Movie(String name) {
// System.out.println("电影准备开始");
// System.out.println("广告回来,下集更精彩");
// System.out.println("下一集开始了");
this.name = name;
System.out.println("Movie(String name)构造器被调用");
}
{
System.out.println("电影准备开始");
System.out.println("广告回来,下集更精彩");
System.out.println("下一集开始了");
}
public Movie(String name, double price) {
// System.out.println("电影准备开始");
// System.out.println("广告回来,下集更精彩");
// System.out.println("下一集开始了");
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
// System.out.println("电影准备开始");
// System.out.println("广告回来,下集更精彩");
// System.out.println("下一集开始了");
this.name = name;
this.price = price;
this.director = director;
System.out.println("Movie(String name, double price, String director)构造器被调用");
}
}
运行结果:
电影准备开始
广告回来,下集更精彩
下一集开始了
Movie(String name)构造器被调用
==============================
电影准备开始
广告回来,下集更精彩
下一集开始了
Movie(String name, double price, String director)构造器被调用
四、1、代码块的使用注意事项和细节:
- static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次;
- 如果是普通代码块,每创建一个对象(new),就执行一次。
- 如果只是使用类的静态成员时,普通代码块并不会被执行。(可以这么理解,普通代码块就是构造器的补充,构造器被调用,普通代码块就被调用)
public class block03 {
public static void main(String[] args) {
DD dd = new DD();
System.out.println("=======================");
DD dd1 = new DD();
System.out.println("=======================");
// 如果只使用类的静态代码块,普通代码块并不会被执行
System.out.println(DD.n1);
}
}
class DD{
public static int n1=99999;
// 静态代码块
static {
System.out.println("DD静态代码块被执行");
}
{
System.out.println("DD普通代码块被加载");
}
}
运行结果:
DD静态代码块被执行
DD普通代码块被加载
=======================
DD普通代码块被加载
=======================
99999
- 什么时候类加载(重点)
- 1)创建对象实例的时候(也就是new),类会被加载
- 2)创建子类对象实例,父类也会被加载。【
父类先
被加载,子类后
被加载】 - 3)使用类的静态成员时(静态属性或者静态方法),类会被加载(普通代码块不会被加载)
代码演示:
注意:这里代码在不继承父类的时候,就会只加载类的本身(创建对象实例的时候(也就是new),类会被加载)
public class block02 {
public static void main(String[] args) {
//什么时候类被加载
// 1. 创建对象实例的时候(new),类被加载
AA aa = new AA();
// 2.创建子类对象实例,父类也会被加载
// 继承的本质:先加载父类后加载子类
// 3. 使用类的静态成员的时候(静态属性,静态方法),类被加载
System.out.println("=====================================");
System.out.println(Cat.n1);
}
}
class Animal{
static {
System.out.println("Animal的静态代码块被执行");
}
}
class Cat extends Animal{
public static int n1=999;
static {
System.out.println("Cat的静态代码块被调用");
}
}
class BB{
static {
System.out.println("父类BB的静态代码块被加载");
}
}
class AA extends BB{
// 静态代码块
static {
System.out.println("静态代码块AA被执行");
}
}
运行结果:
父类BB的静态代码块被加载
静态代码块AA被执行
=====================================
Animal的静态代码块被执行
Cat的静态代码块被调用
999
3. 创建一个对象时,在一个类的调用顺序是:(重点)
- 第一:调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级是一样的,如果有多个静态代码块和多个静态属性初始化,则按照它们
定义的顺序
调用,谁先定义,谁先调用)
public class block04 {
public static void main(String[] args) {
C c = new C();
}
}
class C{
private static int n1=getN1();
static {
System.out.println("C 静态代码块");
}
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
}
- 第二:调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级是一样的,如果有多个普通代码块和多个普通属性初始化,则按照它们
定义的顺序
调用,谁先定义,谁先调用) - 第三:调用构造器
public class block04 {
public static void main(String[] args) {
C c = new C();
}
}
class C{
// 构造器
public C() {
System.out.println("C()构造器被调用");
}
// 普通属性
private int n2=getN2();
//普通代码块
{
System.out.println("C的普通代码块");
}
// 普通方法
private int getN2(){
System.out.println("getN2被调用");
return 200;
}
//静态属性
private static int n1=getN1();
// 静态代码块
static {
System.out.println("C 静态代码块");
}
// 静态方法
public static int getN1(){
System.out.println("getN1(静态)被调用");
return 100;
}
}
运行结果:
getN1(静态)被调用
C 静态代码块
getN2被调用
C的普通代码块
C()构造器被调用
4. 构造器的内部:构造器的前面其实隐含了super()和调用普通代码块,静态代码块和静态属性在类加载时,就执行完毕,因此优先于构造器和普通代码块执行的。
public BBB() {
// 构造器里面隐藏了一些语句:
// 1)super();
// 2)调用本类的普通代码块
System.out.println("BBB() 构造器被调用");
}
public class block05 {
public static void main(String[] args) {
new BBB();
}
}
class AAA{
{
System.out.println("AAA的普通代码块");
}
public AAA(){
// 构造器里面隐藏了一些语句:
// 1)super();
// 2)调用本类的普通代码块
System.out.println("AAA() 构造器被调用");
}
}
class BBB extends AAA{
{
System.out.println("BBB的普通代码块。。。");
}
public BBB() {
// 构造器里面隐藏了一些语句:
// 1)super();
// 2)调用本类的普通代码块
System.out.println("BBB() 构造器被调用");
}
}
运行结果:
AAA的普通代码块
AAA() 构造器被调用
BBB的普通代码块。。。
BBB() 构造器被调用
5. 创建一个子类对象时(继承关系),静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序
:
1)父类的静态代码块和静态属性(优先级一样,具体情况按定义顺序进行)
2)子类的静态代码块和静态属性(优先级一样,具体情况按定义顺序进行)
3)父类的普通代码块和普通属性初始化(优先级一样,具体情况按定义顺序进行)
4)父类的构造方法
5)子类的普通代码块和普通属性初始化(优先级一样,具体情况按定义顺序进行)
6)子类的构造方法
public class block06 {
public static void main(String[] args) {
// 1)进行类的加载
// 1.1 先加载父类-->1.2 后加载子类
// 2)创建对象
// 2.1 从子类的构造器开始
new Son();
}
}
class Father{
private static int m1 =getM1();
static {
System.out.println("2.Father 的一个静态代码块");
}
public static int getM1(){
System.out.println("1.Father的getM1(静态)");
return 100;
}
public int M2=getM2();
{
System.out.println("6.Father的一个普通代码块");
}
public int getM2(){
System.out.println("5.Father的getM2(普通)");
return 200;
}
public Father(){
// 隐藏了super()
// 普通代码块,普通属性的初始化...
System.out.println("7.Father的无参构造器");
}
}
class Son extends Father{
private static int n1=getN1();
static {
System.out.println("4.Son 的一个静态代码块");
}
public static int getN1(){
System.out.println("3.Son的getN1(静态)");
return 100;
}
public int n2=getN2();
{
System.out.println("9.Son的一个普通代码块");
}
public int getN2(){
System.out.println("8.Son的getN2(普通)");
return 200;
}
public Son(){
// 隐藏了super()---->Father
// 普通代码块,普通属性的初始化...
System.out.println("10.Son的无参构造器");
}
}
运行结果:
1.Father的getM1(静态)
2.Father 的一个静态代码块
3.Son的getN1(静态)
4.Son 的一个静态代码块
5.Father的getM2(普通)
6.Father的一个普通代码块
7.Father的无参构造器
8.Son的getN2(普通)
9.Son的一个普通代码块
10.Son的无参构造器
6. 静态代码块只能调用静态成员(静态属性和静态方法),普通代码块可以用任意成员。