一、类变量和类方法
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);//ok
main01.cry();
}
}
三、代码块
3.1、代码块的基本介绍
1、代码化块又称为初始化块,属于类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
2、但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
3.2、代码块的基本语法
[修饰符]{
代码
};
说明注意:
1、“修饰符”可选,要写的话,也只能写 static
2、代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。
3、逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
4、“;”号可以写上,也可以省略。
3.3、代码块的代码块的好处和使用场景
1、相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
注:不管调用哪个构造器,创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器
2、场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
3.4、代码块的使用注意事项和细节讨论(重要)
3.4.1、static代码块
也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。
3.4.2、类什么时候被加载【重要】
①创建对象实例时(new)
②创建子类对象实例,父类也会被加载
③使用类的静态成员时(静态属性,静态方法)
3.4.3、普通的代码块
在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行。
3.4.4、创建一个对象时,在一个类调用顺序是【重点】
①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
③调用构造方法。
5、构造器的最前面其实隐含了 super()和调用普通代码块,新写一个类演示,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
class A {
public A(){//构造器
//这里有隐藏的执行要求
//(1)superO);//这个知识点,在前面讲解继承讲解过
//(2)调用普通代码块的
System.out.println("ok");
}
}
3.4.4.1、示例
我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:【面试题】
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法
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);//ok
m1();
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
}
}
3.4.5、注:
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
四、内部类
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)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
4.5.1、匿名内部类的基本语法:
类或接口 对象名 = new 类或接口 (参数列表){
类体
};
对象名.方法 (new 接口(){
类体
},形参列表)
编译类型:等号左边。
运行类型:匿名内部类(系统生成)。
参数列表传给构造器。继承类实现接口
4.5.2、重要:涉及①继承②多态③动态绑定④内部类
1、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
2、可以直接访问外部类的所有成员,包含私有的
3、不能添加访问修饰符,因为它的地位就是一个局部变量。
4、作用域:仅仅在定义它的方法或代码块中。
5、匿名内部类--访问-->外部类成员 【访问方式:直接访问】
6、外部其他类--不能访问--->匿名内部类(因为 匿名内部类地位是一个局部变量)
7、如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.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;
@Override
public 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();
}
}
1618

被折叠的 条评论
为什么被折叠?



