<Java><!!!><面试题>
装箱 & 拆箱
- public class Test03 {
- public static void main(String[] args) {
- Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
- System.out.println(f1 == f2);
- System.out.println(f3 == f4);
- }
- }
这个题有几个知识点要明确:1)上面4个变量都是object,所以 == 比较的不是值,而是引用。2)要清楚装箱的本质:我们给一个Integer赋int值的时候,会调用Integer类的静态方法valueOf,该方法的源码如下:
- public static Integer valueOf(int i) {
- if (i >= IntegerCache.low && i <= IntegerCache.high)
- return IntegerCache.cache[i + (-IntegerCache.low)];
- return new Integer(i);
- }
其中IntegerCache是Integer的内部类,如果整形字面量的值在-128 ~ 127之间,那么就不会new新的Integer对象,而是直接引用常量池中的Integer对象。
综上,答案是 true & false。
2. 内存中的栈、堆和方法区的用法(JVM)
通常我们定义一个基本数据类型的变量,一个对象的引用,以及函数调用的现场都保存在JVM的栈中。而通过new关键字和构造器创建的对象则放在堆中。
堆是垃圾收集器管理的主要区域由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured。
- 堆、栈、方法区的区别:
- 栈由系统自动分配释放,使用一级缓存,它们通常是被调用时处于存储空间,调用完毕立即释放;每个线程都有一个栈区,只保存基础数据类型和对象的引用。栈分为三个部分:基本类型变量区、执行环境上下文、操作指令区。
- 堆由程序员分配释放,使用二级缓存,生命周期由虚拟机的垃圾回收算法来决定。jvm只有一个堆区,被所有线程共享,堆中只存放对象本身。
- 方法区又叫静态区,也被所有线程共享。方法区包含所有的class和static变量。
- 堆、栈、方法区的区别:
以上,栈空间操作起来最快,但是栈很小,通常大量对象都是放在堆空间,栈和堆的大小都可以通过JVM启动参数来调整。栈用完会引发StackOverflowError,而堆和常量池空间不足会引发OutOfMemoryError。
- String str = new String("hello");
上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区的。
3. 用最有效率的方法计算2乘以8?
答: 2 << 3。在HashCode()的源码中,hashCode的公式是 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] ,为什么这里要使用31?这里有两个考虑:
- 31是素数(质数),使用素数可以降低冲突概率;
- 31 * num 等价于(num << 5) - num,左移5位相当于乘以2的5次方再减去自身就相当于乘以31,现在的VM都能自动完成这个优化。
4. equals & hashCode
两个对象值相同(x.equals(y) == true), 那它们可能有不同hash code吗?
答:不能。Java规定,若两个对象相同(equals返回true),那么它们的hashCode一定相同。但是相反不一定成立。
当然,你未必一定按要求去做,但是如果你违背了上述原则就会发现使用在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(hashCode频繁冲突造成的存取性能急剧下降)。
关于equals方法:必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。
5. 如何实现对象克隆
有两种方式:
1)实现Cloneable接口并重写Object类中的clone()方法;
2)实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。[implements Serializable接口,实现toString()方法]
6. GC
Java提供的gc功能可以自动监测对象是否超过作用域,从而达到自动回收内存的目的。可以通过System.gc()或Runtime().gc()请求垃圾收集,但是JVM可以屏蔽掉显式地垃圾回收调用。
垃圾回收通常作为一个低优先级的线程运行。垃圾回收机制有很多种,包括分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:
- - 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
- - 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
- - 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
7. Java类加载机制
- class A {
- static {
- System.out.print("1");
- }
- public A() {
- System.out.print("2");
- }
- }
- class B extends A{
- static {
- System.out.print("a");
- }
- public B() {
- System.out.print("b");
- }
- }
- public class Hello {
- public static void main(String[] args) {
- A ab = new B();
- ab = new B();
- }
- }
上面这段代码的执行结果是1a2b2b。
类加载机制的相关知识比较多一点,专门整理了一篇博客 点我点我点我
8. String
- private void testString(){
- String s1 = "Programming";
- String s = "Programming";
- String s2 = new String("Programming");
- System.out.println(s2.getClass().toString());
- String s3 = "Program";
- String s4 = "ming";
- String s5 = "Program" + "ming";
- String s6 = s3 + s4;
- System.out.println(s1 == s); // true
- System.out.println(s1 == s2); // false
- System.out.println(s1 == s5); // true
- System.out.println(s1 == s6); // false
- }
对于上述代码:
- s == s1,很好理解,因为他俩都是字符串常量,指向常量池中的同一块空间;
- s1 != s2, 因为s2是在堆中new出来的一块内存
- s1 == s5,因为s5也是字符串常量,编译时被确定,仍然与s1指向同一块常量池空间;
- s1 != s6, 这里由于s3和s4的拼接需要额外创建一个StringBuffer(或StringBuilder),之后再将StringBuffer转换为String,此处很明显new了一个对象,因此是在堆中进行的。(最后得到的s6是StringBuilder.toString()的返回值。看了下源码,返回的是 return new String(value, 0, count);
这里有一个很重要的点,既然直接用 + ,底层也会用StringBuilder去实现,那么为什么在实际中我们通常会说用StringBuilder,而不去用 + 呢?考虑的是这样的情况:
- public class Test4 {
- public static void main(String[] args) {
- String s = "s";
- for (int i = 0; i < 20; i++) {
- s += i;
- }
- }
- }
上述代码中,用String+的方式,每循环一次,就会重新new一个StringBuffer对象,这样的内存消耗完全是不必要的(在数据量大的情况下,还会导致内存不足的错误)。# 据说字符串的加法是java唯一一个实现了运算符重载的地方。
<Java><!!!><面试题>的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- caffe 动态库 Release X64
Release X64平台 createdll.h#ifndef CREARDLL_H_#define CREARDLL_H_ extern "C" _declspec(dllex ...
- 『MXNet』第一弹_基础架构及API
MXNet是基础,Gluon是封装,两者犹如TensorFlow和Keras,不过得益于动态图机制,两者交互比TensorFlow和Keras要方便得多,其基础操作和pytorch极为相似,但是方便不 ...
- 3月26 document的练习
1.Window.document对象 一.找到元素: docunment.getElementById("id"):根据id找,最多找一个: var a =docunme ...
- arguments.callee用法
arguments.callee 在哪一个函数中运行,它就代表哪一个函数. 一般用在匿名函数中. 在匿名函数中有时会需要自己调用自己,但是由于是匿名函数,没有名子,无名可调. 这时就可以用argume ...
- ThinkPHP3的使用
1. 初始目录 7d 根目录 ├─Application 应用目录(空) ├─Public 资源文件目录 ├─ThinkPHP 框架目录 └─index.php 入口文件 2. 入口文件 // 应用入 ...
- Slony-I同步复制部署
本次测试环境 IP 10.189.102.118 10.189.100.195 10.189.100.226 PGHOME /usr/local/pgsql /usr/local/pgsql /usr ...
- Sorting Algorithms
Merge sort by using recursive strategy, i.e. divide and conquer. def merge(left,right): result = [] ...
- 关于ORA-06508 , ORA-04068异常的详细说明
参考:程序包调用报ORA-06508: PL/SQL: 无法找到正在调用的程序单元 出现这种情况的原因是因为,对于全局变量,每一个session会生成一个本地copy,如果程序重新编译的话,就会因程序 ...
- IDEA 自动生成 serialVersionUID 的设置
打开File菜单,选择Settings选项,打开Settings设置对话框:左边树形目录,打开Editor>Inspections
- [contest 782] 9.7
[contest 782] 9.7 - XJOI 个人觉得温暖题啊,,,可是卡毛空间呀!!! T1 传送