Java对象初始化通过构造器,清理通过垃圾收集器(Garbage Collector, GC)
构造器
无参构造函器,有参构造器
如果创建一个类,没有自定义构造器,编译器会自动创建一个无参构造器
方法重载
函数名相同,参数个数或者参数类型不同,也可参数顺序不同(不推荐)
this关键字
- this可以表示当前对象的引用,不能用于静态方法(静态方法与对象无关)
public class Leaf{
int i = 0;
Leaf increment(){
this.i++;
return this;
}
void print(){
System.out.println("i = "+i);
}
public static void main(String [] args){
Leaf x = new Leaf();
x.increment().increment().increment.print();
}
}
- this 关键字在向其他方法传递当前对象时也很有用:
class Person {
public void eat(Apple apple) {
Apple peeled = apple.getPeeled();
System.out.println("Yummy");
}
}
public class Peeler {
static Apple peel(Apple apple) {
// ... remove peel
return apple; // Peeled
}
}
public class Apple {
Apple getPeeled() {
return Peeler.peel(this);
}
}
public class PassingThis {
public static void main(String[] args) {
new Person().eat(new Apple());
}
}
- this 在构造器中如果被传入一个参数列表,则是通过最直接的方式显示得调用匹配参数列表得构造器
public class Flower {
private int preCount = 0;
String s = "initial value";
Flower(int preCount){
this.preCount = preCount;
System.out.println("Constructor w/ int arg only, preCount = " + preCount);
}
Flower(String s){
System.out.println("Constructor w/ string arg only, s = " + s);
this.s = s;
}
Flower(int preCount, String s){
this(preCount);
this.s = s;
System.out.println("String & int args");
}
Flower(){
this(7,"hi");
System.out.println("no-arg constructor");
}
public void print(){
System.out.println("preCount = " + preCount + " s = " + s);
}
public static void main(String []args){
Flower flower1 = new Flower();
flower1.print();
// Tree tree1 = new Tree(1);
// flower1.print();
// flower1.preCount = 2;
// flower1.print();
}
}
class Tree{
private int height;
Tree(int height){
this.height = height;
}
}
- 成员变量和参数列表中变量同名,用this.s来表明指代得是成员变量,防止混淆
在构造器中调用构造器
- 在构造器中可以通过this加参数列表的形式调用其他构造器
- 在一个构造器内只可以通过this调用一次其他的构造器
- 必须首先调用构造器
- 不能在非构造器的方法中调用构造器
static的含义
- 不能在静态方法中调用非静态方法和this,后两者与对象有关,静态方法与对象无关
- 非静态方法可以调用静态方法
- 静态方法只能调用静态变量
垃圾回收器(Garbage Collector)
- 垃圾回收器一边回收内存,一边使堆中的对象紧凑排列
- 引用计数,每个对象有一个引用计数器,每当有引用指向该对象时,引用计数加1,当引用离开作用域或者置null时,引用计数器减1。当引用计数器为0时,释放其内存空间
- 如果从栈或静态存储区出发,遍历所有的引用,你将会发现所有"活"的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是该对象包含的所有引用,如此反复进行,直到访问完"根源于栈或静态存储区的引用"所形成的整个网络。
- Java虚拟机采用一种自适应的垃圾回收技术,停止-复制(stop-and-copy)和标记-清扫(mark-and-sweep)相结合。
"标记-清扫"所依据的思路仍然是从栈和静态存储区出发,遍历所有的引用,找出所有存活的对象。但是,每当找到一个存活对象,就给对象设一个标记,并不回收它。只有当标记过程完成后,清理动作才开始。在清理过程中,没有标记的对象将被释放,不会发生任何复制动作。"标记-清扫"后剩下的堆空间是不连续的,垃圾回收器要是希望得到连续空间的话,就需要重新整理剩下的对象
构造器初始化
- 自动初始化发生在构造器初始化之前
- 类中变量定义初始化在构造器之前
- 静态的基本类型,如果你没有初始化它,那么它就会获得基本类型的标准值,如果是对象引用,就是null。
加载类->如果类是第一次加载则初始化静态成员变量->初始化非静态成员变量 - 导致类加载的原因: 1、new 一个类的对象,(构造器其实也是静态方法) ; 2、调用类的静态方法和静态成员变量
- 创建一个类的对象过程:
1、构造器其实也是静态方法,加载类,Java解释器在类路径中查找,定位java.com.util...class;
2、加载类之后,首先初始化静态成员变量(如果是第一次加载类);
3、当通过new 方法创建对象,在堆上分配内存空间;
4、分配的存储空间首先会被清零,对象的基本类型数据设置为默认值,引用设置为null;
5、执行所有出现在字段定义处的初始化动作;
6、执行构造器。 - 静态块和其他静态初始化动作一样,仅在第一次类加载时执行
- 只有new对象的类加载才会初始化非静态属性
数组初始化
- 静态数组初始化
int [] a = {1,2,3,4,5}; - 动态数组初始化
int [] a = new int[5];
int [] intA = new int[5];
System.out.println(intA[0]);
System.out.println(Arrays.toString(intA));
System.out.println("*********");
char [] charA = new char[5];
System.out.println(charA[0]);
System.out.println(Arrays.toString(charA));
System.out.println("*********");
Character [] characterA = new Character[5];
System.out.println(characterA[0]);
System.out.println(Arrays.toString(characterA));
System.out.println("*********");
Integer [] IntegerA = new Integer[5];
System.out.println(IntegerA[0]);
System.out.println(Arrays.toString(IntegerA));
结果:
0
[0, 0, 0, 0, 0]
*********
[ , , , , ]
*********
null
[null, null, null, null, null]
*********
null
[null, null, null, null, null]
可变参数列表
import java.util.Arrays;
public class OverloadingVarargs2 {
static void f(float i, Character... args) {
System.out.println(i);
System.out.println(Arrays.toString(args));
System.out.println("first");
}
static void f(Character... args) {
System.out.println("second");
}
// 修改为下面函数就不会报错
// static void f(char c, Character... args) {
// System.out.println("second");
// }
public static void main(String[] args) {
// f(1, 'a');
f('a', 'b');
// System.out.println((float) 'r');
}
}
会报错
Error:(19, 9) java: 对f的引用不明确
OverloadingVarargs2 中的方法 f(float,java.lang.Character...) 和 OverloadingVarargs2 中的方法 f(java.lang.Character...) 都匹配
因为char向上转换(隐式转换)到float和装箱为Character优先级相同,如果改为注释,char能找到对应的参数类型,就不会产生歧义。
枚举类型 enum
public enum Course {
ENGLISH, CHINESE, MATH, PHYSICAL,
}
public class EnumTest {
Course takeCourse;
EnumTest(Course takeCourse) {
this.takeCourse = takeCourse;
}
public static void main(String[] args) {
Course course1 = Course.CHINESE;
System.out.println(course1);
for (Course c1 : Course.values())
System.out.println(c1.ordinal());
EnumTest test1 = new EnumTest(Course.CHINESE);
EnumTest test2 = new EnumTest(Course.ENGLISH);
EnumTest test3 = new EnumTest(Course.MATH);
test1.printTakeCourse();
test2.printTakeCourse();
test3.printTakeCourse();
}
private void printTakeCourse() {
switch (this.takeCourse) {
case ENGLISH:
System.out.println("take english");
break;
case CHINESE:
System.out.println("take chinese");
break;
default:
System.out.println("take other courses");
break;
}
}
}
- enum类型适用于switch;
- values() 方法按照 enum 常量的声明顺序,生成这些常量值构成的数组;
- ordinal() 方法表示某个特定 enum 常量的声明顺序。
网友评论