一、类变量和类方法
1.1、类变量
1.1.1、类变量内存布局(静态变量放在哪里?)
1、JVM7及以前的近代变量放在方法区中;JVM8以后的静态变量放在堆中
2、不管static变量在哪里,共识:
1)Static变量是同一个类所有对象共享的
2)Static类变量在类加载的时候就生成了
1.1.2、什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
1.1.3、如何定义类变量
1、定义语法
访问修饰符 static 数据类型变量名;
static 访问修饰符 数据类型变量名;
2、访问类变量
类名.类变量名
对象名.类变量名
1.1.4、类变量使用注意事项和细节讨论
1、什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name, static fee)
2、类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
3、加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
4、类变量的定义不能放在方法里面,因它是属于类的。可以在方法中使用类变量,但不能定义类变量
5、类变量可以通过 “类名.类变量名”或者“对象名.类变量名”来访问,但java设计者推荐我们使用“类名.类变量名”方式访问。【前提是满足访问修饰符的访问权限和范围】
6、实例变量不能通过类名.类变量名方式访问。
7、类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。
8、.类变量的生命周期是随类的加载开始,随着类消亡而销毁。
public class StaticDetail {public static void main(String[] args) {B b = new B();//System.out.println(B.n1);System.out.println(B.n2);//静态变量是类加载的时候,就创建了,所以我们没有创建对象实例//也可以通过类名.类变量名来访问System.out.println(C.address);}
}class B {public int n1 = 100;public static int n2 = 200;
}class C {public static String address = "北京";
}
1.2、类方法
1.2.1、类方法基本介绍
1、类方法也叫静态方法。
2、定义语法
访问修饰符 static 数据返回类型方法名 (){}
static 访问修饰符 数据返回类型方法名 (){}
1.2.2、类方法的调用【前提是满足访问修饰符的访问权限和范围】
类名.类方法名
对象名.类方法名
1.2.3、类方法的经典使用场景
1、当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
2、比如:工具类中的方法 utils;Math类、Arrays类、Collections集合类看下源码
3、小结:在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等
1.2.4、类方法使用注意事项和细节讨论
1、类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数;普通方法中隐含着this的参数
2、类方法可以通过类名调用,也可以通过对象名调用。
3、普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
4、类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
5、类方法(静态方法),只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
public class StaticMethodDetail {public static void main(String[] args) {D.hi();//ok//非静态方法,不能通过类名调用//D.say();, 错误,需要先创建对象,再调用new D().say();//可以}
}
class D {private int n1 = 100;private static int n2 = 200;public void say() {//非静态方法,普通方法}public static void hi() {//静态方法,类方法//类方法中不允许使用和对象有关的关键字,//比如this和super。普通方法(成员方法)可以。//System.out.println(this.n1);}//类方法(静态方法)中 只能访问 静态变量 或静态方法//口诀:静态方法只能访问静态成员.public static void hello() {System.out.println(n2);System.out.println(D.n2);//System.out.println(this.n2);不能使用hi();//OK//say();//错误}//普通成员方法,既可以访问 非静态成员,也可以访问静态成员//小结: 非静态方法可以访问 静态成员和非静态成员public void ok() {//非静态成员System.out.println(n1);say();//静态成员System.out.println(n2);hello();}
}
二、理解 main 方法语法
2.1、深入理解 main 方法
public static void main(String[] args){}
1、main方法时虚拟机调用
2、java虚拟机需要调用类的main/方法,所以该方法的访问权限必须是public
3、java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
4、该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
IDEA接收参数:editor Configurations -> program arguments
5、java 执行的程序 参数1 参数2 参数3
2.1、特别提示:
1、在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。
2、但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
public class Main01 {//静态的变量/属性private static String name = "韩顺平教育";//非静态的变量/属性private int n1 = 10000;//静态方法public static void hi() {System.out.println("Main01的 hi方法");}//非静态方法public void cry() {System.out.println("Main01的 cry方法");}public static void main(String[] args) {//可以直接使用 name//1. 静态方法main 可以访问本类的静态成员System.out.println("name=" + name);hi();//2. 静态方法main 不可以访问本类的非静态成员//System.out.println("n1=" + n1);//错误//cry();//3. 静态方法main 要访问本类的非静态成员,需要先创建对象 , 再调用即可Main01 main01 = new Main01();System.out.println(main01.n1);//okmain01.cry();}
}
三、代码块
3.1、代码块的基本介绍
1、代码化块又称为初始化块,属于类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
2、但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
3.2、代码块的基本语法
[修饰符]{
代码
};
说明注意:
1、“修饰符”可选,要写的话,也只能写 static
2、代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。
3、逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
4、“;”号可以写上,也可以省略。
3.3、代码块的代码块的好处和使用场景
1、相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
注:不管调用哪个构造器,创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器
2、场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
3.4、代码块的使用注意事项和细节讨论(重要)
1、static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。
2、类什么时候被加载【重要】
①创建对象实例时(new)
②创建子类对象实例,父类也会被加载
③使用类的静态成员时(静态属性,静态方法)
3、普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行。
小结123:
1)static代码块是类加载时,执行,只会执行一次
2)普通代码块是在创建对象时调用的,创建一次,调用一次3.类加载的3种情况,需要记住.
4、创建一个对象时,在一个类调用顺序是:【重点,难点】
①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
③调用构造方法。
5、构造器的最前面其实隐含了 super()和调用普通代码块,新写一个类演示,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
class A {public A(){//构造器//这里有隐藏的执行要求//(1)superO);//这个知识点,在前面讲解继承讲解过//(2)调用普通代码块的System.out.println("ok");}
}
6、我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:【面试题】
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法
package com.hspedu.codeblock_;public class CodeBlockDetail04 {public static void main(String[] args) {//(1) 进行类的加载//1.1 先加载 父类 A02 1.2 再加载 B02//(2) 创建对象//2.1 从子类的构造器开始//new B02();//对象new C02();}
}class A02 { //父类private static int n1 = getVal01();static {System.out.println("A02的一个静态代码块..");//(2)}{System.out.println("A02的第一个普通代码块..");//(5)}public int n3 = getVal02();//普通属性的初始化public static int getVal01() {System.out.println("getVal01");//(1)return 10;}public int getVal02() {System.out.println("getVal02");//(6)return 10;}public A02() {//构造器//隐藏//super()//普通代码和普通属性的初始化......System.out.println("A02的构造器");//(7)}
}class C02 {private int n1 = 100;private static int n2 = 200;private void m1() {}private static void m2() {}static {//静态代码块,只能调用静态成员//System.out.println(n1);错误System.out.println(n2);//ok//m1();//错误m2();}{//普通代码块,可以使用任意成员System.out.println(n1);System.out.println(n2);//okm1();m2();}
}class B02 extends A02 {private static int n3 = getVal03();static {System.out.println("B02的一个静态代码块..");//(4)}public int n5 = getVal04();{System.out.println("B02的第一个普通代码块..");//(9)}public static int getVal03() {System.out.println("getVal03");//(3)return 10;}public int getVal04() {System.out.println("getVal04");//(8)return 10;}public B02() {//构造器//隐藏了//super()//普通代码块和普通属性的初始化...System.out.println("B02的构造器");//(10)// TODO Auto-generated constructor stub}
}
7、静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
四、内部类
4.1、基本介绍
1、一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
2、是我们类的第五大成员【类的五大成员是哪些?属性、方法、构造器、代码块、内部类】
3、内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,后面看底层源码时,有大量的内部类
4.2、基本语法
class Outer{//外部类class Innerf{//内部类}
}
class Other{//外部其他类}
4.3、内部类的分类
4.3.1、定义在外部类局部位置上(比如方法内)
1、局部内部类(有类名)
2、匿名内部类(没有类名,重点!!!)
4.3.2、定义在外部类的成员位置上
1、成员内部类(没用static修饰)
2、静态内部类(使用static修饰)
4.4、局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1、可以直接访问外部类的所有成员,包含私有的
2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
3、作用域:仅仅在定义它的方法或代码块中。
4、局部内部类--访问-->外部类的成员【访问方式:直接访问】
5、外部类--访问-->局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
注意:(1)局部内部类定义在方法中/代码块(2)作用域在方法体或者代码块中(3)本质仍然是一个类
6、外部其他类--不能访问-->局部内部类(因为局部内部类地位是一个局部变量)
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class LocalInnerClass {//public static void main(String[] args) {//演示一遍Outer02 outer02 = new Outer02();outer02.m1();System.out.println("outer02的hashcode=" + outer02);}
}class Outer02 {//外部类private int n1 = 100;private void m2() {System.out.println("Outer02 m2()");}//私有方法public void m1() {//方法//1.局部内部类是定义在外部类的局部位置,通常在方法//3.不能添加访问修饰符,但是可以使用final 修饰//4.作用域 : 仅仅在定义它的方法或代码块中final class Inner02 {//局部内部类(本质仍然是一个类)//2.可以直接访问外部类的所有成员,包含私有的private int n1 = 800;public void f1() {//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,// 使用 外部类名.this.成员)去访问// Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);System.out.println("Outer02.this hashcode=" + Outer02.this);m2();}}//6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可Inner02 inner02 = new Inner02();inner02.f1();}
}
4.5、匿名内部类的使用(重要!!!)
(1)本质是类(2)内部类(3)该类没有名字(4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
1、匿名内部类的基本语法:
类或接口 对象名 = new 类或接口 (参数列表){
类体
};对象名.方法 (new 接口(){
类体
},形参列表)
编译类型:等号左边。
运行类型:匿名内部类(系统生成)。
参数列表传给构造器。继承类实现接口
重要!!!:涉及①继承②多态③动态绑定④内部类
2、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
3、可以直接访问外部类的所有成员,包含私有的
4、不能添加访问修饰符,因为它的地位就是一个局部变量。
5、作用域:仅仅在定义它的方法或代码块中。
6、匿名内部类--访问-->外部类成员 【访问方式:直接访问】
7、外部其他类--不能访问--->匿名内部类(因为 匿名内部类地位是一个局部变量)
8、如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
package com.hspedu.innerclass;public class AnonymousInnerClassDetail {public static void main(String[] args) {Outer05 outer05 = new Outer05();outer05.f1();//外部其他类---不能访问----->匿名内部类System.out.println("main outer05 hashcode=" + outer05);}
}class Outer05 {private int n1 = 99;public void f1() {//创建一个基于类的匿名内部类//不能添加访问修饰符,因为它的地位就是一个局部变量//作用域 : 仅仅在定义它的方法或代码块中Person p = new Person(){private int n1 = 88;@Overridepublic void hi() {//可以直接访问外部类的所有成员,包含私有的//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +" 外部内的n1=" + Outer05.this.n1 );//Outer05.this 就是调用 f1的 对象System.out.println("Outer05.this hashcode=" + Outer05.this);}};p.hi();//动态绑定, 运行类型是 Outer05$1//也可以直接调用, 匿名内部类本身也是返回对象// class 匿名内部类 extends Person {}
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");}
}class Person {//类public void hi() {System.out.println("Person hi()");}public void ok(String str) {System.out.println("Person ok() " + str);}
}
//抽象类/接口...
4.6、成员内部类的使用
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。
1、可以直接访问外部类的所有成员,包含私有的
2、可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
3、作用域:和外部类的其他成员一样,为整个类体,在外部类的成员方法中创建成员内部类对象,再调用方法。
4、成员内部类--访问-->外部类成员(比如:属性)【访问方式:直接访问】
5、外部类--访问-->成员内部类【访问方式:创建对象,再访问】
6、外部其他类---访问--->成员内部类
7、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class MemberInnerClass01 {public static void main(String[] args) {Outer08 outer08 = new Outer08();outer08.t1();// 外部其他类,使用成员内部类的三种方式// 第一种方式// outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员// 这就是一个语法,不要特别的纠结.Outer08.Inner08 inner08 = outer08.new Inner08();inner08.say();// 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象Outer08.Inner08 inner08Instance = outer08.getInner08Instance();inner08Instance.say();}
}class Outer08 { //外部类private int n1 = 10;public String name = "张三";private void hi() {System.out.println("hi()方法...");}//1.注意: 成员内部类,是定义在外部内的成员位置上//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员public class Inner08 {//成员内部类private double sal = 99.8;private int n1 = 66;public void say() {//可以直接访问外部类的所有成员,包含私有的//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.//,可以通过 外部类名.this.属性 来访问外部类的成员System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1);hi();}}//方法,返回一个Inner08实例public Inner08 getInner08Instance(){return new Inner08();}//写方法public void t1() {//使用成员内部类//创建成员内部类的对象,然后使用相关的方法Inner08 inner08 = new Inner08();inner08.say();System.out.println(inner08.sal);}
}
4.7、静态内部类的使用
说明:静态内部类是定义在外部类的成员位置,并且有static修饰
1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2、可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
3、作用域:同其他的成员,为整个类体
4、静态内部类--访问--->外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
5、外部类--访问-->静态内部类【访问方式:创建对象,再访问】
6、外部其他类---访问--->静态内部类
方式1:因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer.Inner inner = new Outer.Inner();
方式2:编写一个方法(或静态方法),可以返回静态内部类的对象实例.
Outer10 outer10 = new Outer10();Outer10.Inner10 inner101 = outer10.getInner10();
Outer10.Inner10 inner10_ = Outer10.getInner10_();//【无需创建对象】
7、如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
public class StaticInnerClass01 {public static void main(String[] args) {Outer10 outer10 = new Outer10();outer10.m1();//外部其他类 使用静态内部类//方式1//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)Outer10.Inner10 inner10 = new Outer10.Inner10();inner10.say();//方式2//编写一个方法(或静态方法),可以返回静态内部类的对象实例.Outer10.Inner10 inner101 = outer10.getInner10();System.out.println("============");inner101.say();Outer10.Inner10 inner10_ = Outer10.getInner10_();System.out.println("************");inner10_.say();}
}class Outer10 { //外部类private int n1 = 10;private static String name = "张三";private static void cry() {}//Inner10就是静态内部类//1. 放在外部类的成员位置//2. 使用static 修饰//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员//5. 作用域 :同其他的成员,为整个类体static class Inner10 {private static String name = "韩顺平教育";public void say() {//如果外部类和静态内部类的成员重名时,静态内部类访问的时,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)System.out.println(name + " 外部类name= " + Outer10.name);cry();}}public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问Inner10 inner10 = new Inner10();inner10.say();}public Inner10 getInner10() {return new Inner10();}public static Inner10 getInner10_() {return new Inner10();}
}