java static 获取当前类对象,Java笔记(七)——类和对象(下) static关键字 /访问限制符 /内部类 /栈 堆...

1 包

包可以说,就是目录,和磁盘上的目录是一一对应的。

导入包:

例如,scanner在使用时,我们就需要导入(import java.util.Scanner;),导入后才能正常使用。我们自己创建的包之间也可以这样导入,做法相同,都可以在输入时按Tab 键直接导入,或者用Alt + Enter 进行导入。

关于Scanner详细可看《Java : Scanner用法 干货 简明》。

2 static 关键字

2.1 static 修饰变量

如果类中的某个成员加上了static ,说明这个成员是一个 类属性/类方法;如果没有static ,说明这个成员是一个实例属性/实例方法。

例子:

public class Cat {

String name;

String gender;

public Cat(String name, String gender) {

this.name = name;

this.gender = gender; //实例属性

public static int n = 0; //类属性

this.eat("鱼");

}

public void eat(String food){

System.out.println(name + "正在吃" + food);

}

public class Main {

public static void main(String[] args) {

Cat cat = new Cat("糯米","公猫");

Cat cat2 = new Cat("粽子","母猫");

}

}

例子中,我们创建了两个Cat 类的对象,其中的name、和gender属性是实例属性,和对象相关。也就是不同的对象可以有不同的属性,一只是叫糯米的公猫,一只是叫粽子的母猫。

其中的n = 0 是类属性,和类相关和实例无关,我们可以通过 Cat.n 的方式访问并且修改n值。

Cat.n = 100;

System.out.println(Cat.n);

Cat 类就相当于模具,根据它会做出不同颜色、口味的猫抓点心,在这里是不同颜色、性别的猫。

a82eb97fda159ccc54de8fe1204403ec.png

2.2 static 修饰方法

public static void func(){

System.out.println("这是static 修饰的方法");

this.name; // 错误!!!

}

Cat.func();

我们写一个被static 修饰的方法,然后进行调用,注意通过类Cat 进行调用,不需要 Cat cat = new Cat (“糯米”,“公猫”) 这样的创建实例,它是被static修饰的类相关的方法,不需要实例也可以进行调用。

然后,注意在static修饰的静态方法中,不能使用this,this指向当前的实例,依托于实例,而static修饰的方法和实例无关,只和类有关。同理,无法在static 方法中访问非 static的变量的方法。

2.3 static 修饰代码块

static {

//静态代码块

//静态代码块,只在类加载的时候执行一次

System.out.println("这是静态代码块。");

}

{

System.out.println("这是普通代码块。");

}

Cat cat = new Cat("糯米","公猫");

Cat cat2 = new Cat("粽子","公猫");

46184e075b83eac6174ab8fc00403e00.png

静态代码块,只在程序启动的时候(类加载的时候)执行一次。通过上述例子,我们不难看出,静态代码块只执行了一次,而普通代码块执行了两次,因为创建了两个实例,创建几个实例,普通代码块就执行多少次。

3 访问限制符

前提条件:两个类,一个类A,一个调用类A的类B。

1、public:修饰的成员可以被外部的类随意访问。

public class A {

public int num = 0;

}

public class B {

public static void main(String[] args) {

A a = new A();

System.out.println(a.num); // 能正确访问

}

}

2、private:只能在A类中访问到

(一个成员,能是private,就尽量使用private)

public class A {

private int num = 0;

// 只能在A 类中使用

public static void main(String[] args) {

A a = new A();

System.out.println(a.num);

}

}

public class B {

public static void main(String[] args) {

A a = new A();

System.out.println(a.num); // 不能正确访问

}

}

3、default:在同一个包中就可以访问到

public class A {

int num = 0;

}

4、protected :可以被同包的其他类访问,也可以被其他包的子类访问。

访问权限高低:

public > protected > default > private

4 内部类

一个类的定义在另一个类的里面。

1、普通内部类 / 成员内部类

public class A {

class Test{

public int num;

}

public void func() {

Test t = new Test();

t.num = 10;

}

public static void main(String[] args) {

Test test = new Test(); // 错误 !!!

}

}

内部类Test 作为外部类A 的成员,它就要依托于 this 被引用,而在外部类A的static 修饰的方法是类相关的方法,没有this ,导致无法实例化。

2、静态内部类(内部类前用 static)

静态内部类不依赖外部类的 this,可以随意创建。

public class Main {

static class Test{ //加 static

public int num;

}

public void func() {

Test t = new Test();

t.num = 10;

}

public static void main(String[] args) {

Test test = new Test(); // 正确

}

}

3、匿名内部类(相对比较常用)

public class Main {

static class Test{ //加 static

public int num;

}

public void func() {

Test t = new Test();

t.num = 10;

}

public static void main(String[] args) {

//匿名内部类

A a = new A(){

//定义属性和方法

};

}

}

匿名内部类,没有名字,是A类的子类(继承自A类)。

4、局部内部类

直接定义到方法的内部。

public class Main {

static void main(String[] args) {

Test test = new Test();

// 局部内部类

class Test{

}

}

}

5 类和对象的内存布局

1、前提条件:两个类,一个类Test,一个类Main,在类Main 中创建一个方法func2,该方法创建了Test 类的对象,并输出对象的num。在主方法中调用func2 。

public class Test {

public int num = 100;

}

public class Main {

public static void func2(){

Test t = new Test();

System.out.println(t.num);

}

public static void main(String[] args) {

func2();

}

}

6dd28302a62267d4dfc272f4429b858c.png

首先,程序运行找主方法,主方法进栈,然后主方法中调用func2方法,func2方法进栈,在func2方法中创建了对象。

在创建对象的时候,新创建出来的对象在 堆 上面的 0x100位置,然后栈中的引用存着它的引用 t 0x100,引用指向堆上的实例。关于引用以及具体的入栈出栈《Java笔记(三) —— 方法调用/入栈 出栈 栈帧/重载问题》《Java笔记 (五)—— 引用类型》

2、前提条件:一个 Main 类,一个 B 类,一个 A 类。Main 类中有方法func2 创建了类 B 的对象,并在主函数中调用;类 B中创建了类 A 的实例作为成员;类 A 中创建了String 的实例 " hello " 作为成员 。

public class Main {

public static void func2(){

B b = new B();

}

public static void main(String[] args) {

func2();

}

}

public class B {

public A a = new A();

}

public class A {

tring str = "hello";

}

f60298b7fe734d12924e621dfb0563a7.png

(1) 程序运行,从主方法开始,主方法进栈;

(2) 主方法调用func2方法,func2方法进栈;

(3) func2 方法根据类 B 创建了对象(实例),创建出来的 B 的实例 b 在堆上的 0x100,栈帧中存放着实例的引用 b,该引用保存着实例在堆上的地址 0x100,指向在堆上的实例 b;而实例 b 中根据类 A 创建了实例,创建出来的 A 的实例 a 在堆上的 0x200,实例 b 中存放着实例 a 的地址0x200;

(4) 实例 a 创建了 String 的实例,创建出来的实例 str 在堆上的0x300,实例 a 中存放着 String 实例的地址 0x300;

(5) 堆上的0x300 放着创建的 String 的实例 " hello "。

3、上面两个例子讲了栈和堆,还有一个很重要的区域:方法区。方法区里存着的是一个一个 “ 类相关的信息 ”(每个类的方法的二进制的指令也是在这里)

(1) 如果属性是一个实例属性,那么不在方法区,是跟着实例走的,实例一般在 堆上;

(2)如果属性是一个类属性,那么就在 方法区;

(3)对于方法来说,不管是加 static 还是不加,对应的内容都在方法区。

前提条件:一个Main 类,一个 A 类,Main 类的主方法创建了类 A 的实例。

public class Main {

public static void main(String[] args) {

A a = new A();

}

}

public class A {

static public int num = 100;

static public String str = "hello";

public int num2 = 200;

public void func(){

//

}

}

3e28a79967d3f3cd2cf47e7b0ce5f6ac.png

main方法入口,main方法进栈,然后main方法创建了实例 a,该实例的地址存在栈帧的引用 a 中 0x100,实例存在堆上,num2 = 200为实例属性跟着实例走,所以在堆 上;A 类中的方法func 存在方法区中; A 类中的其他属性因为被 static 修饰为类属性,所以在方法区中,但注意static 修饰的String ,是将引用 str 0x200 存在方法区,由String 创建的实例str 在堆上。

看明白了会发现,有点意思,冲冲冲~

4e464e87b2aa3b9bcb7de0b17ed6a62c.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值