同步操作将从 Java精选/Ebooks 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
Integer是int的包装类,默认值是null;int是基本数据类型,默认值是0;
Integer变量必须实例化后才能使用;int变量不需要;
Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值。
分析总结
1)Integer与new Integer不相等。new出来的对象被存放在堆,而非new的Integer常量则在常量池,两者内存地址不同,因此判断是false。
2)两个值都是非new Integer,如果值在-128,127区间,则是true,反之为false。
这是因为java在编译Integer i2 = 128时,被翻译成:
Integer i2 = Integer.valueOf(128);
而valueOf()函数会对-128到127之间的数进行缓存。
3)两个都是new Integer,两者判断为false,内存地址不同。
4)int和Integer对比不管是否new对象,两者判断都是true,因为会把Integer自动拆箱为int再去比。
Java中Integer a=128与Integer b=128是不相等。
Integer a= 127;
Integer b= 127;
Integer c= 128;
Integer d= 128;
System.out.println(a==b); //返回true;
System.out.println(c==d); //返回false;
System.out.println(a.equals(b)); //返回true;
System.out.println(c.equals(d)); //返回true;
引用类型中==是比较的对象内存地址;而基本数据类型中==是比较的值。
IntegerCache.low默认是-128;IntegerCache.high默认是127。
如果Integer类型的值是-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,因此超过范围c==d的结果是false。
不可变对象是指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象。
比如String、Integer及其它包装类。
不可变对象最大的好处是线程安全。
静态变量:独立存在的变量,只是位置放在某个类下,可以直接类名加点调用静态变量名使用。并且是项目或程序一启动运行到该类时就直接常驻内存。不需要初始化类再调用该变量。用关键字static声明。静态方法也是同样,可以直接调用。
实例变量:相当于该类的属性,需要初始化这个类后才可以调用。如果这个类未被再次使用,垃圾回收器回收后这个实例也将不存在了,下次再使用时还需要初始化这个类才可以再次调用。
1)存储区域不同:静态变量存储在方法区属于类所有,实例变量存储在堆当中;
2)静态变量与类相关,普通变量则与实例相关;
3)内存分配方式不同。
4)生命周期不同。
需要注意的是从静态变量在jdk7以后和字符串常量池一起存储在了堆中,JDK1.8开始用于实现方法区的PermSpace被MetaSpace取代。
&是位操作
&&是逻辑运算符
逻辑运算符具有短路特性,而&不具备短路特性。
来看一下代码执行结果:
public class Test{
static String name;
public static void main(String[] args){
if(name!=null & name.equals("")){
System.out.println("ok");
}else{
System.out.println("error");
}
}
}
执行结果:
Exception in thread "main" java.lang.NullPointerException
at com.jingxuan.JingXuanApplication.main(JingXuanApplication.java:25)
上述代码执行时抛出空指针异常,若果&替换成&&,则输出日志是error。
RMI体系结构分以下几层:
存根和骨架层(Stub and Skeleton layer): 这一层对程序员是透明的,它主要负责拦截客户端发出的方法调用请求,然后把请求重定向给远程的RMI服务。
远程引用层(Remote Reference Layer): RMI体系结构的第二层用来解析客户端对服务端远程对象的引用。这一层解析并管理客户端对服务端远程对象的引用。连接是点到点的。
传输层(Transport layer): 这一层负责连接参与服务的两个JVM。这一层是建立在网络上机器间的TCP/IP连接之上的。它提供了基本的连接服务,还有一些防火墙穿透策略。
两个对象hashCode()相同,使用equals()方法判断不一定为true。
两个对象hashCode()相同,只能说明哈希值相同,不代表这两个键值对相等。
String str1 = "通话";
String str2 = "重地";
// str1: 1179395 | str2: 1179395
System.out.println(String.format("str1: %d | str2: %d",str1.hashCode(),str2.hashCode()));
// false
System.out.println(str1.equals(str2));
自动装箱是指将基本数据类型重新转化为对象。
public class Test {
public static void main(String[] args) {
Integer num = 9;
}
}
num = 9的值是属于基本数据类型,原则上不能直接赋值给对象Integer。但是在JDK1.5版本后就可以进行这样的声明自动将基本数据类型转化为对应的封装类型,成为对象后可以调用对象所声明的方法。
自动拆箱是指将对象重新转化为基本数据类型。
public class Test {
public static void main(String[] args) {
// 声明Integer对象
Integer num = 9;
// 隐含自动拆箱
System.out.print(num--);
}
}
由于对象不能直接进行运算,而是需要转化为基本数据类型后才能进行加减乘除。
// 装箱
Integer num = 10;
// 拆箱
int num1 = num;
java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。加载并初始化类完成后,再进行对象的创建工作。
我们先假设是第一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。
一、类加载过程(第一次使用该类)
java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程:
双亲委托模型的工作过程是:如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。 使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
1、加载
由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例
2、验证
格式验证:验证是否符合class文件规范 语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同) 操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
3、准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内) 被final修饰的static变量(常量),会直接赋值;
4、解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。 解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定
以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
5、初始化(先父后子)
1)为静态变量赋值;
2)执行static代码块。
注意:static代码块只有jvm能够调用。如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过,父类初始化时,子类静态变量的值也有有的,是默认值。
最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
二、创建对象
1、在堆区分配对象需要的内存:
分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量。
2、对所有实例变量赋默认值:将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值。
3、执行实例初始化代码:初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法。
4、如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它。需要注意的是,每个子类对象持有父类对象的引用,可在内部通过super关键字来调用父类对象,但在外部不可访问。
补充:
通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。
如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。
static关键字修饰的变量或方法是属于类的,在编译时就已经确定了;而普通变量或方法是属于该由类生成的对象,需要在实例化后才能确定。
因此,若static关键字修饰了方法的局部变量,一方面方法需要在实例化之后才能确定,另一方面static修饰的变量需要在编译时确定,这就会导致矛盾。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。