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 类就相当于模具,根据它会做出不同颜色、口味的猫抓点心,在这里是不同颜色、性别的猫。

在这里插入图片描述

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("粽子","公猫");

在这里插入图片描述

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

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();
	}
}

在这里插入图片描述
  首先,程序运行找主方法,主方法进栈,然后主方法中调用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";
}

在这里插入图片描述

(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(){
    //
    }
}

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

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

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值