文章目录
要不要在for循环内部new
第一种情况 对于new的调用只存在于for循环以内
对象的保存位置是在堆内存中, p只是一个记录了堆地址的4个字节的引用 p存在的位置是栈内存
基与这个点我们可以画图表示下面的内存情况
public void demo() {
Persion p = null;
for (int i = 0; i < 1000; i++) {
p = new Persion();
p.setAge(i);
}
}
public void demo() {
for (int i = 0; i < 1000; i++) {
Persion p = new Persion();
p.setAge(i);
}
}
对象new了1000次
public void demo1() {
Persion p = new Persion();
for (int i = 0; i < 10; i++) {
p.setAge(i);
}
}
对象new了1次
通过比对就可以很明显的发现第三种方法是最节省jvm内存的.第一种和第二种的区别在于栈空间内变量引用的数据,第一种只有一个变量,第二种有1000个变量.
第二种情况 对于new的调用不止存在于for循环以内外部也有调用
对于这种情况上面的第三种写法就不可以 只能是前两种写法
public void demo() {
List list= new ArrayList();
Persion p = null;
for (int i = 0; i < 10; i++) {
p = new Persion();
p.setAge(i);
list.add(p);
}
list.stream().forEach(per -> {
// 业务处理
System.out.println(per.toString());
});
}
p只是一个引用,每次循环保存到集合里面的都是一个新的地址值
public void demo() {
List list= new ArrayList();
for (int i = 0; i < 10; i++) {
Persion p = new Persion();
p.setAge(i);
list.add(p);
}
list.stream().forEach(p -> {
// 业务处理
System.out.println(p.toString());
});
}
基与java gc的考虑
第一 堆的角度 因为堆内存中必定要分配1000个对象大小的空间,两者的是没有区别的
第二 生命周期角度 第一个对象的作用域是整个方法 第二个对象的作用域是在for循环以内
他们不可达阶段是一致的,(不可达的对象才会被gc)
对象的生命周期 创建 -> 应用 -> 不可见 -> 不可达 -> 收集 ->终结 ->对象内存空间重新分配
基与栈的考虑
栈的管理是jvm自己管理的,没有gc的参与,一个线程对应一个栈,一个方法对应一个栈帧,方法执行完成之后自动出站,栈是先进后出,栈内存的大小可以通过jvm配置的参数来设定 -Xss256k,栈内存的溢出一般都是因为递归调用太深,或者递归调用使用不当.一个方法里面定义的变量再这个方法出站的时候,就一起跟着消失了.
所以我认为上面的两种写法几乎是没有差别的.
new 和 clone的区别 哪个更快
new一个对象的过程
默认这个类第一次被加载 之前没有加过,new对象的时候首先会检查class是否被加载到方法区,如果加载过了就不会在加载,如果没有加载过回重新加载
1.加载:类加载器把类加载到jvm内并将类的信息保存到方法区
2.验证 准备 解析 这三个过程也叫做链接
3.初始化:初始化的时候为static 变量 static{代码块}赋值 先父类初始化赋值 再后子类初始化赋值
1->3是类加载的过程
4.给对象分配堆内存 有两种方式 指针碰撞 空闲列表
5.给变量赋默认值 从方法区内拷贝变量的引用 给堆内的对象赋默认值
6.最后执行构造方法 先执行父类的构造方法再执行子类的构造方法,如果继承关系比较负载父类还有父父父父类的话,要从顶级的父类开始执行,这种情况是比较耗时的.
clone 对象
Java 中的clone 需要对象实现 Cloneable
clone 默认是浅拷贝 对于基本数据类型拷贝的是值,对于引用数据类型拷贝的是引用,两个clone的对象内部还是存在联系的.
String 是引用数据类型 但是String很特别是不能被修改的,"zhangsan"是不能被修改的,所以这个例子中不会有什么问题.
如果name属性换成一个Car对象 car里面有color 如果p1修改了自己的car的颜色,p2的car颜色也会被修改所以存在一定的隐患
public class Persion implements Cloneable {
private String name;
private int age;
private Car car;
@Override
protected Persion clone() throws CloneNotSupportedException {
return (Persion)super.clone();
}
public class Car {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public static void main(String[] args) throws Exception{
Persion p1= new Persion();
Car car= new Car();
car.setColor("yellow");
p1.setCar(car);
p1.setName("zhangsan");
Persion p2 = p1.clone();
System.out.println(p1==p2);
System.out.println(p2.toString());
p1.getCar().setColor("red");
System.out.println(p2.toString());
}
执行结果
false
Persion{name='zhangsan', age=0,car.color=yellow}
Persion{name='zhangsan', age=0,car.color=red}
clone与new的区别在于clone不用执行对象的构造方法,当对象的继承关系复杂 父类和自己的构造方法内操作比较多(比如初始化了好多的对象);这种情况clone的效率会明显高于new,如果是一个简单的对象简单的构造方法,初始化的是时间基本一样(亲自测过 clone 不见得一定快);