1、构造器的作用
- 构造器采用与类名相同的名称,初始化期间自动调用构造器:new Rock()将会为对象分配内存空间,并调用相应的构造器;
- 构造器有助于减少错误,使代码易于阅读;
创建一个类,它包含一个在定义时就被初始化了的String域,以及另一个通过构造器初始化的String域,两种方式有何差异?
-> 定义时期的初始化次数为1次
构造器初始化为两次:① 首先编译器发现变量未初始化,赋予正确的值;
② 在构造方法中再次赋值;
2、什么是重载
重载即一个类中的多种构造器(含有相同的名字/类名)有不同种参数同时存在,只能以参数的不同来区分(参数顺序不同也是不同的构造器)。
class Flower {
grow() {
print("还是花苞");
}
grow(String s) {
print(s + "开花了!");
}
}
3、this关键字
3.1 this的用法
- 在方法内部,this表示“”调用方法的那个对象“的引用,例如当需要返回当前对象的引用时;
public class Leaf {
int i;
Leaf increment() {
i++;
return this;
}
}
- this关键字对于将当前对象传递给其他方法也很有用;
package nuc.test;
class Person {
public void eat(Apple apple) {
Apple peeled = apple.getPeeled(); //将apple对象传递给peeler的peel方法
System.out.println("Yummy");
}
}
class Apple {
Apple getPeeled() {
return Peeler.peel(this);
}
}
class Peeler {
static Apple peel(Apple apple) {
//remove peel
return apple; //peeled
}
}
public class test1 {
public static void main(String[] args) {
new Person().eat(new Apple());
}
}
- 在构造器中调用构造器
public class test1 {
public test1() {
// TODO Auto-generated constructor stub
this("炜炜");
}
test1(String s) {
System.out.println("希望未来有你," + s);
}
public static void main(String[] args) {
test1 t = new test1();
}
}
- 编译器禁止在任何方法中调用构造器
3.2 遗留问题
- 为什么可以用this调用一个构造器,却不能调用两个?
- 为什么必须将构造器调用放在最起始处?
- 为什么禁止在其他方法中调用构造器?
答:
- 一个对象只能实例化一次,也就是说,由某个类创建的对象要唯一;
- Java在操作对象之前,需要确保每个对象都能够得到实例化,因此调用相应的构造器确保初始化的进行;
- 如果在其他方法中调用了构造器,那么实例化对象之后,若调用含有构造器的该方法,会导致对象再次被编译器默认初始化;(目前自己这么理解,若有补充或错误请及时指正)
4、static方法
4.1 定义
- 就是没有this的方法;
- 在static方法中不能调用非静态方法;
- 可以在不创建对象的前提下,仅仅通过类本身来调用static方法;
- 不可以在static方法中定义static变量,但可以引用,static变量又叫做类变量,就是说它和方法是处在同一个位置的,没有包含被包含的关系。方法中可以引用static变量,但是不可以在方法中定义即使static变量即使该方法是static方法~ 同样的main里面也不可以;
class test2 {
static void test1() {
System.out.println("test2()");
}
}
public class test1 {
public static void main(String[] args) {
test2.test1();;
}
}
4.2 为什么static里面不能含有this
因为static方法和类的实例(对象)是两码事,它只在类装载的时候(即编译的时候或者javac命令的时候)初始化,被称作类级变量(属于类);而类的实例是在程序运行的时候(即Java命令的时候)初始化,被称作对象级变量(属于对象);
this表示这个类的当前实例,super表示父类的当前实例,static是属于类的,this是类的一个对象,当然调用了不了他,static太牛了,只有类名可以调用它,static叫静态方法,也叫类方法,就是在程序启动的时候,就会为这个方法分配一块内存空间,所以什么时候都可以调用这个方法。所以,静态方法里不能调用非静态方法,除非你先实例化那个类,像这样:
package nuc.housy; import static nuc.test.Print.*; public class test1 { public static void tes() { test1 t = new test1(); t.tes1(); print("我是静态方法"); } void tes1() { print("我是非静态方法"); } public static void main(String[] args) { print("我是一"); } }
如果在static修饰的方法中使用this关键字,而这个关键字就无法指向合适的对象;所以我们也说,静态成员不能直接访问非静态成员;jvm有类加载器,第一次加载类时执行类中的static域,jvm会专门划分一个内存区域给static程序块,可以成为静态区,属于这个类。this指针是指向类的对象,在实例化对象时jvm会在堆区分配内存给一个具体的对象,this指针指向这个对象。而类中的static域始终是在静态区分配内存,this指向堆区,所以不能调用。static是属于类的,this是对象指针
而堆不是在静态存储区的,属于动态存储区,所谓静态,就是会永恒存在、不会消失,这样的数据包括常量、常变量、静态变量、全局变量等;动态的区域,就是堆栈,在第一章里写到过:
- 栈区:也叫栈内存,主管java程序的运行,实在线程创建的时候创建,他的生命周期是跟随线程的生命周期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,生命周期与线程一致,基本类型的变量和对象的引用变量都是在函数的栈内存中分配,分为三个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
- 堆区:是jvm中最大的,应用的对象与数据都存储在这个区域,这块区域也是线程共享的,也是gc主要回收区,一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:新生代、年老代、永久代。
- 方法区:方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中。
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
5、清理和垃圾回收(一般不用)
finalize()方法类似于c++中的析构函数;
- 析构函数,在对象销毁时自动调用,若不销毁则不调用;
- finalize方法,一旦垃圾回收器准备释放对象占用的存储空间,首先调用finalize方法;
6、垃圾回收的工作原理 (没有太明白)
回收机制名称 | 优点 | 缺点 | 简要说明 |
引用计数 | 简单、速度慢 | 若对象存在循环引用时,工作量很大 | 每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加一,当引用离开作用域时或被置为null,引用计数减一;
|
自适应 | ① 停止 - 复制 ② 标记 - 清扫 |
7、成员初始化
- 默认初始化:对象默认成null,其他基本类型默认给定一个正确的值;
- 指定初始化:即字面量;
- 构造器初始化:在自动初始化之后执行;
7.1初始化顺序
- 非静态数据初始化:在调用构造器或者其他方法之前初始化;
- 静态数据初始化:静态数据初始化只有在必要时刻才进行,如果不创建带有静态数据的对象,也不再调用带有静态数据的对象的方法,将不会初始化;
- 初始化顺序:先静态对象,而后非静态对象;
- 显示静态初始化:尽管看起来像个方法,实际姿势一段跟在static关键字后面的代码。与其他初始化动作一样,这段代码仅执行一次:首次生成这个类的一个对象时,或者首次访问属于这个类的静态数据成员时(即便从未生成过这个对象)
public class Spon { static int i; static { i = 47; } }
------->这篇博客有详细说明:https://blog.csdn.net/hou_shiyu/article/details/95002777
-
非静态实例初始化:这种语法对支持匿名内部类是必须的,且无论调用了哪个显式构造器,某些操作都会发生,可以理解成非静态变量的初始化执行多次;
public class Spon { int i; { i = 47; } }
8、数组
8.1 数组初始化
int[] a1; [1]
int a1[]; [2]
int[] a1 = {1,2,3,4};
int[] a2 = a1; //这样只是复制了一个引用,也就是说,a2指向的值若发生改变,a1中也会发生变化
Integer[] a = {
new Integer(1),
new Integer(2),
3,
} //最后的逗号可以省略 [3]
Integer[] b = new Integer[] {
new Integer(1),
new Integer(2),
3,
} [4]
8.2 可变参数列表
- 可变参数列表不依赖于自动包装机制
class test2 {
static void test1(int...args) {
System.out.println(args.length);
}
}
class test3 {
static void test1(Integer...args) {
System.out.println(args.length);
}
}
public class test1 {
public static void main(String[] args) {
test2.test1(1,2,3);
test2.test3(1,2);
}
}
- 自动包装机制会有选择地将int参数提升为Integer
9、enum
- ordinal() : 表示特定的enum常量的声明顺序;
- 结合switch使用;