7.static成员
7.1static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,
是所有对象所共享的。
【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
class Student {
public String name;
public int age;
public int id;
public static String school = "西安";//学校 不属于对象 属于类(公用的静态的成员变量[方法区]
//生命周期伴随一生
public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
System.out.println(Student.classRoom);
Student s1 = new Student("Li leilei", "男", 18, 01);
Student s2 = new Student("Han MeiMei", "女", 19, 02);
// 也可以通过对象访问:但是classRoom是三个对象共享的
System.out.println(s1.classRoom);
System.out.println(s2.classRoom);
//System.out.println(student1.school);//不合理 不合法 不建议这样写
System.out.println(Student.school);//合理的 建议这样(用类名)访问静态成员
}
}
7.2 static修饰成员方法
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中
classRoom属性如何在类外访问呢?
class Student {
private String name;//封装:对外隐藏了类内部实现的细节
private int age; //需要set和get
private int id;
public static String school = "西安";
public Student() {
System.out.println("不带参数的构造方法!");
}
public class TestStudent {
public static void main(String[] args) {
System.out.println(Student.classRoom);
}
}
}
编译失败:Error:(10, 35) java: classRoom 在 extend01.Student 中是 private 访问控制
那static属性应该如何访问呢?
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态
成员一般是通过静态方法来访问的.
public static String getClassRoom(){
return classRoom;
}
我们就需要借助get和set来访问了。
【静态方法特性】
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量
public static String getClassRoom(){
System.out.println(this);
return classRoom;
}
编译失败:Error:(35, 28) java: 无法从静态上下文中引用非静态 变量 this
public static String getClassRoom(){
age += 1;
return classRoom;
}
编译失败:Error:(35, 9) java: 无法从静态上下文中引用非静态 变量 age
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法
传递this引用
public static String getClassRoom(){
doClass();
return classRoom;
}
编译报错:Error:(35, 9) java: 无法从静态上下文中引用非静态 方法 doClass()
5. 静态方法无法重写,不能用来实现多态.
7.3 static成员变量初始化
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实
例属性。
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
1. 就地初始化
就是在在定义时直接给出初始值。
private static String classRoom = "Bit306";
2. 静态代码块初始化
在类中直接已用static直接定义:
static {
school = "hafuo";
System.out.println("静态代码块 -》定义在类内部,和构造方法同级,主要初始化静态成员");
}
7.4static修饰的变量计算问题
class Test {
public int a1;
public static int a2;
}
public class TextDemo2 {
public static void main(String[] args) {
Test test1 = new Test();
test1.a1++;
test1.a2++;
Test test2 = new Test();
test2.a1++;
test2.a2++;
System.out.println("a1="+test1.a1);
System.out.println("a2="+Test.a2);
}
}
a1=1
a2=2static是在方法区存储的。
8. 代码块
8.1 代码块概念以及分类
分类:普通代码块
构造块
静态块
同步代码块
class Student {
private String name;
private int age;
private int id;
public static String school = "西安";
public Student() {
System.out.println("不带参数的构造方法!");
}
public Student(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
System.out.println("带有三个参数的构造方法!");
}
{
this.name = "实例(构造)代码块";
System.out.println("实例代码块 -》定义在类内部,和构造方法同级");
}
static {
//school = "hafuo";
System.out.println("静态代码块 -》定义在类内部,和构造方法同级,主要初始化静态成员");
}
public void sleep() {
{
int x = 10;//作用域
System.out.println("普通代码块");
}
//System.out.println(x);
}
public static void main4(String[] args) {
Student student1 = new Student();
System.out.println("===========");
Student student2 = new Student();
//先静态在实例,静态只执行一次,在类加载的时候就被执行了。
}
}
8.2 普通代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例
成员变量
【注意事项 】
实例代码块优先于构造方法执行,因为编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法第一条语句前.
8.4 静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量
注意事项
静态代码块不管生成多少个对象,其只会执行一次
1.静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
2.Java代码在经过编译器编译之后,如果要运行必须先要经过类加载子系统加载到JVM中才能运
行。在加载阶段:
3.在链接阶段第二步准备中会给静态成员变量开辟空间,并设置为默认值,在初始化阶段,会执行
静态代码块中的代码。(了解:关于类加载过程后序JVM中会详细讲解)
4.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并,最
终放在生成的<>方法中,该方法在类加载时调用,并且只调用一次。
5.实例代码块只有在创建对象时才会执行.
综合这些概念和代码的使用,我们更加清晰的了解了代码块这一知识点。
9. 内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为
外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在
另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
但是有些定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
public class A{
}
class B{
}
A 和 B是两个独立的类,彼此之前没有关系
内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件 。
9.1 内部类的分类
public class OutClass {
// 成员位置定义:未被static修饰 ---> 普通内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
public void method(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5{
}
}
}
根据内部类定义的位置不同,一般可以分为以下几种形式:
1. 成员内部类(普通内部类:未static修饰的成员内部类 和 静态内部类:被static修饰的成员内部类)
2. 局部内部类(不谈修饰符)、匿名内部类
注意:内部类其实日常开发中使用并不是非常多,大家在看一些库中的代码时候可能会遇到的比较
多,日常开始中使用最多的是匿名内部类。
9.2 成员内部类
在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类。
9.1.1 普通内部类
即未被static修饰的成员内部类。
class OuterClass {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
//实例内部类
class InnerClass {
public int date1 = 1000;
public int date4 = 4;
private int date5 = 5;
//public static int date6 = 6; //实例内部类需要对象引用,与static冲突 但是可以用
//static final 修饰。
public static final int date6 = 6;
public void test() {
System.out.println(OuterClass.this.date1);
System.out.println(date2);
System.out.println(date3);
System.out.println(this.date4);
System.out.println(this.date5);
System.out.println(date6);
System.out.println("InnerClass的test()方法");
}
}
public void test2() {
OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
System.out.println(innerClass2.date1);
System.out.println("OutClass的test()方法");
}
public static void main(String[] args) {
//InnerClass innerClass = new OutClass.InnerClass();
OuterClass outerClass = new OuterClass();
System.out.println(outerClass.date1);
//System.out.println(outerClass.date1);
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.test();
System.out.println(innerClass.date4);
OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
innerClass2.test();
}
}
实例内部类的引用格式: OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
外部类的引用与普通类相同。
【注意事项】
1. 外部类中的任何成员都可以被在普通内部类方法中直接访问
2. 普通内部类所处的成员与外部类成员位置相同,因此也受public、private等访问限定符的约束
3. 在内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外
部类名称.this.同名成员 来访问
4. 普通内部类对象必须在先有外部类对象前提下才能创建
5. 普通内部类的非静态方法中包含了一个指向外部类对象的引用
6. 外部类中,不能直接访问内部类中的成员,如果要访问必须先要创建外部类的对象。
9.1.2 静态内部类
即被static修饰的内部成员类称为静态内部类。
class OuterClass2 {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
//静态内部类
static class InnerClass2 {
public int date1 = 1000;
public int date4 = 4;
private int date5 = 5;
public static final int date6 = 6;
//public OuterClass2 out = new OuterClass2();//(2)
public OuterClass2 out;//(3)
public InnerClass2() {
}
public InnerClass2(OuterClass2 out) {
this.out = out;
}
public void text() {
//OuterClass2 outerClass2 = new OuterClass2();//(1)
System.out.println(date1);
System.out.println(out.date2);
System.out.println(date3);//可以
System.out.println(this.date4);
System.out.println(this.date5);
System.out.println(date6);
System.out.println("InnerClass的test()方法");
}
}
public static void main1(String[] args) {
OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
//此时不需要拿到外部类对象,更加有效
}
}
我们在内部要访问外部的成员的方法:
1,
class OuterClass2 {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
//静态内部类
static class InnerClass2 {
//public int date1 = 1000;
//public int date4 = 4;
//private int date5 = 5;
//public static final int date6 = 6;
public OuterClass2 out = new OuterClass2();
public void text() {
System.out.println(date3);//可以
}
}
2,
class OuterClass2 {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
public InnerClass2() {
}
public InnerClass2(OuterClass2 out) {
this.out = out;
}
public void text() {
System.out.println(date3);//可以
}
}
【注意事项】
1. 在内部类中只能访问外部类中的静态成员
2. 创建内部类对象时,不需要先创建外部类对象
3. 成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:外部类名称$内部类名称
9.3 局部内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式:
public class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
【注意事项】
1. 局部内部类只能在所定义的方法体内部使用
2. 不能被public、static等修饰符修饰
3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$x内部类名字.class,x是一个整数。
4. 几乎不会使用
9.4 匿名内部类
=========================== 接口 =========================
10. 对象的打印
public class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public static void main(String[] args) {
Person person = new Person("77","男", 18);
System.out.println(person);
}
}
打印结果:day20210829.Person@1b6d3586
但是我们想要打印 “"77","男", 18”,我们只能自己写一个print方法。
public void print() {
System.out.println(" 姓名:"+name+" 年龄:"+age+" ID:"+id);
}
但是我们发现这样的话,如果数据量一大,会发现写起来十分困难。那么我们就又有一种简便的方
法来供我们使用。
单击鼠标右键,选择Generate---->继续选择toString();即可。
@Override //----》用来检验重写。
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
END