第11篇 类对象

1. 静态方法与非静态方法的区别

  • 静态方法在定义类的时候就随着类装载到了内存中,不会自动销毁,直到关闭 jvm
  • 非静态方法只有实例化对象的时候才会分配内存,与实例化对象共存。
public class Demo01 {
    //静态方法 修饰符 static
    public static void user(){
        //sayHello1();//报错,静态方法不能直接调用非静态方法,因为其还没有分配内存,只能通过实例区调用。
        sayHello2();
    }
    //非静态方法
    public void sayHello1(){ }
    public static void  sayHello2(){ }
    public void sayHello3(){
        user();//非静态方法可以直接调用静态方法
    }
}

2. 构造器(c++构造函数)

  • 如果不显式地定义构造函数,编译时编译器会自动生成一个无代码块的无参构造函数
  • 编写时如果添加了有参构造方法而未添加无参构造方法,那么编译器不会再默认添加无参构造方法。
  • 如果只定义有参不定义无参,且后续还需要调用无参构造方法,编译器会报错!
/*例如,如果不显式调用父类有参构造函数,且没写父类的无参构造函数的情况下:
子类会默认调用父类的无参构造函数,但是编译器无法找到一个无参构造函数,会报错!
*/

public class A{
    int a;
    public A(int a){this.a =a;}
}

public class B extends A{
	//以下演示编译器默认的子类B的无参构造函数
    public B(){
        super();//编译器默认在方法体第一行调用父类的无参构造函数。找不到则报错!
    }
} 
    //想要不报错,有以下两个方法:
    //1.自己写B的构造函数,且在方法体第一行写A类的有参构造函数
    public class B extends A{
        //以下演示编译器默认的子类B的无参构造函数
        public B(int a){
            super(a);//父类有参
        }
    } 

    //2.在A类中追加一个无参构造函数
    public class A{
        int a;
        public A(int a){this.a =a;}
        public A(){};//追加无参
    }

3.修饰符

public, protected, private (与C++一致)

public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
	//alt+insert可以快速选择生成getName()和setName函数
    //通过规定对外接口,限制外部对内部变量的随便访问
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.代码块

class Person{
	//匿名代码块
	{
		System.out.println("匿名代码块");
	}
	//加了Static为静态代码块
	static{
		System.out.println("静态代码块");
	}
	//无参构造函数
	public Person(){
		System.out.println("构造方法");
	}
}
//调用以下函数生成一个Person实例会产生什么结果呢?即上述三者的运行先后次序是什么呢?
new Person();
/*
静态代码块
匿名代码块
构造方法
*/

//再执行一次呢?
new Person();
/*
匿名代码块
构造方法
*/
  • 直接由{ }括起来的代码为代码块
  • 普通代码块(叫匿名代码块),再每次生成对象实例的时候都会执行一次,而且是先于构造函数执行
  • 静态代码块,即 static { },只有第一次定义实例的时候会执行,且其执行次序先于普通的代码块和构造方法
  • 匿名代码块常用于赋初始值

5. final修饰符用于类

  • final 可以用于修饰类,放在class之后,类名之前

  • 被 final 修饰的类没有子类

class final Person{}
class Man extends Person{}//报错!Person已经是fianl,它不能生成子类

6.类实例化的过程

[原帖链接] (https://blog.csdn.net/qq_41860497/article/details/130335081)

6.0 双亲委派机制

JVM提供以下三种classLoader 用于把class文件加载到JVM中:

  • Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。

  • ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。

  • AppClassLoader:主要负责加载应用程序的主函数类

    一个自编的class文件经过以下流程进入JVM:
    image

6.1 判断对象是否已经加载、链接、初始化

  • 首先根据new对象的类名是否在常量池中定位到一个符号引用,并检查这个符号引用代表的类是否已经加载、解析和初始化。其实就是验证是否是第一个使用该类。如果是第一次使用该类,就会执行类的加载过程。

  • JVM加载一个类的时候会创建一个instanceKlass,用来表示这个类的元数据,包括常量池、字段、方法等。存放在方法区。

6.2 在 java堆 中创建对象

  • 在new一个对象时,jvm创建instanceOopDesc,来表示这个对象,存放在堆区,其引用存放在栈区;平时说的Java Object Layout就是instanceOopDesc,它用来表示对象的实例信息;
  • instanceOopDesc对应java中的对象实例,包括对象头(其中包含类定义的引用Klass,Klass 中定义了类方法),实例数据(对象真正存储的有效信息,包括程序代码中定义父类子类的各种类型的字段)。
posted @ 2023-12-04 17:45  问稻  阅读(28)  评论(0)    收藏  举报