个人记录 转自r2coding
new一个对象
Sheep sheep1 = new Sheep();Sheep sheep2 = new Sheep( "codesheep", 18, 65.0f );
通过new的方式,我们可以调用类的无参或者有参构造方法来实例化出一个对象
其中的过程:
- 类加载 - 》new一个对象时,查看该对象的引用所代表的类是否已经被加载过了
- 声明类引用 -》声明对象的类型,Sheep 类型的 sheep1 的引用
- 堆内存分配 - 》jvm分配内存
- 属性默认值初始化 - 》实例化对象的各个属性值为默认值,例如 int 是0 ,对象就是null
- 对象头设置 - 》jvm进行对象头设置,这里面就主要包括对象的运行时数据(比如Hash码、分代年龄、锁状态标志、锁指针、偏向线程ID、偏向时间戳等)以及类型指针(JVM通过该类型指针来确定该对象是哪个类的实例);
- 属性显式初始化 -》属性的显式初始化也好理解,比如定义一个类的时候,针对某个属性字段手动的赋值,如:private String name = “codesheep”; 就在这时候给初始化上;
-
反射一个对象
拿到class类的方式
类.class
- 对象名.getClass();
- class.forName(“全类名”);
使用class类的 newInstance( ) 方法进行获取对象
构造无参的对象
Sheep sheep3 = (Sheep) Class.forName( "cn.codesheep.article.obj.Sheep" ).newInstance();
Sheep sheep4 = Sheep.class.newInstance();
构造有参的对象
Constructor<?>[] constructors = Sheep.class.getDeclaredConstructors();
Sheep sheep5 = (Sheep) constructors[0].newInstance();
Sheep sheep6 = (Sheep) constructors[1].newInstance( "codesheep", 18, 65.1f );
指定构造参数类型
Constructor constructor = Sheep.class.getDeclaredConstructor( String.class, Integer.class, Float.class );
Sheep sheep7 = (Sheep) constructor.newInstance( "codesheep", 18, 65.2f );
克隆出一个对象
利用Object的clone方法进行对象克隆
涉及到浅拷贝和深拷贝的问题
值类型和引用类型
Java中,像数组、类Class、枚举Enum、Integer包装类等等,就是引用类型;
诸如int这些基本类型,称它为值类型;
java中只有值传递
值传递是传递实参副本,函数修改不会影响实参;
引用传递是传递实参地址,函数修改会影响实参。
对于对象作为实参传入后,对象只是它的属性被修改,对象的地址其实没有改变,对于函数来说,对象的实参是这个地址
赋值 vs 浅拷贝 vs 深拷贝
赋值:
Student codeSheep = new Student();
Student codePig = codeSheep;
这种不是对象拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象:
浅拷贝
浅拷贝属于对象克隆方式的一种,重要的特性体现在这个 「浅」 字上。
比如我们试图通过studen1实例,拷贝得到student2,如果是浅拷贝这种方式,大致模型可以示意成如下所示的样子:
值类型的字段会复制一份,引用类型的字段拷贝的仅仅是引用地址
String 存在于堆内存、常量池;这种比较特殊, 本身没有实现 Cloneable, 传递是引用地址;
由本身的final性, 每次赋值都是一个新的引用地址,原对象的引用和副本的引用互不影响。
因此String就和基本数据类型一样,表现出了”深拷贝”特性.
深拷贝
深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本,就像这个样子:
反序列化出一个对象
Unsafe黑魔法
sun.misc.Unsafe包路径下的Unsafe类提供了一种直接访问系统资源的途径和方法,可进行底层的操作。
分配内存、创建对象、释放内存、定位对象某个字段的内存位置并修改等等
Unsafe类中allocateInstance()方法,可以创建一个对象。
获取Unsafe类的实例对象,调用allocateInstance()来创建对象
public final class Unsafe {
private static final Unsafe theUnsafe;
// ... 省略 ...
private static native void registerNatives();
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
// ... 省略 ...
}
可以看到Unsafe是单例的,无法通过构造来new,且getUnsafe()上也做了特殊标记,只能从引导加载的类才可以调用,该方法是供JVM内部使用的,外部代码直接使用会报异常
Exception in thread "main" java.lang.SecurityException: Unsafe
可以通过反射来创建Unsafe实例
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
接下来我们就可以愉快地利用它来创建对象了
Sheep sheep8 = (Sheep) unsafe.allocateInstance( Sheep.class );
对象的隐式创建场景
除了几种显式地对象创建之外,还有没有进行手动对象创建的隐式场景,举例:
Class类实例隐式创建
JVM虚拟机在加载一个类时,也会创建该类对应的一个class实例
字符串隐式创建
String string ="";
或者
String str = str1+str2;
自动装箱机制
Integer codeSheepAge = 18; //int类型自动装箱,Integer被隐式的创建了出来
函数可变参数
public double avg( int... nums ) {
double sum = 0;
int length = nums.length;
for (int i = 0; i<length; ++i) {
sum += nums[i];
}
return sum/length;
}
// 函数的调用处可以传入各种离散参数参与计算:
avg( 2, 2, 4 );
avg( 2, 2, 4, 4 );
avg( 2, 2, 4, 4, 5, 6 );
// 隐式地产生一个对应的数组对象进行计算。
