● 请你说一下Java里integer和int的区别,以及如何比较相等
参考回答:
Integer和int的区别:
1、integer是int的包装类,int是Java的一种基本数据结构
2、integer变量必须实例化后才能使用,int变量不需要
3、integer实际是对象的引用,int是直接存储数据值
4、integer的默认值是null,int的默认值是0
如何比较相等,首先要明白equals和==的区别
Equals通常用来比较两个对象的内容是否相等,
==用来比较两个对象的地址是否相等,
通常会认为两个对象的内容相等时,则两个对象相等,equals返回true。对象内容不同,则返回false。
所以可以总结为两种情况
1、类未复写equals方法,则使用equals方法比较两个对象时,相当于==比较,即两个对象的地址是否相等。地址相等,返回true,地址不相等,返回false。
2、类复写equals方法,比较两个对象时,则走复写之后的判断方式。通常,我们会将equals复写成:当两个对象内容相同时,则equals返回true,内容不同时,返回false。
● 请你介绍下Java学习情况,学习一门新的语言需要多快
参考回答:
如实回答即可,体现出一个学习能力的地方。
● 请你介绍一下gc,另外如果Java里写一个方法,这个方法里只有一条语句,即new一个对象,请问方法结束以后这个对象怎么回收的?
参考回答:
GC(garbage collection)垃圾收集,是指JVM用于释放那些不再使用的对象所占用的内存,常用的JVM都有GC,而且大多数gc都使用类似的算法管理内存和执行手机操作,GC可以通过确定对象是否被活动对象引用来确定是否收集该对象,常用的方法有引用计数和对象引用遍历
● 请你回答一下protected,public,private的区别
参考回答:
● 请你回答一下Java的内存结构是什么,全局变量,临时变量,静态变量分别存在哪里,堆分为哪几块,比如说新生代老生代,那么新生代又分为什么
参考回答:
代码区:是编译器生成的一个exe区段,存放函数体的二进制代码
栈区:存放函数的参数,局部变量的值等,其操作方式类似于数据结构中的栈,const局部变量也是放在栈里
堆区:就是malloc和new之类的内存所在区段,一般由程序员分配释放,分配方式类似于链表
静态数据区:是编译器生成的一个exe区段,初始和未初始化的全局变量和局部变量都放在这里,
常量区:是编译器生成的一个exe区段,const全局变量也放在常量区。
全局变量,临时变量,静态变量分别存在哪里
局部变量保存在栈中,全局变量和静态变量存储在静态数据区。
堆分为哪几块,比如说新生代老生代,那么新生代又分为什么?
java垃圾收集管理器的主要区域,因此很多时候被称为“GC堆”。
分为新生代和老年代;
新生代分为:Eden和Survivor。
● 请你回答一下c++和java的区别
参考回答:
1、C++创建对象后需要在使用结束后调用delete方法将其销毁,Java有垃圾回收机制,用来监视new出来的所有对象,辨别不会再被引用的对象,然后释放内存空间
2、当变量作为类的成员使用时,Java才确保给定默认值,以确保那些基本类型的成员变量得到初始化,但是C++没有此功能,
3、c++引入了操作符重载机制,Java不支持
4、Java中没有sizeof(),在C++中sizeof()操作符能够告诉我们为数据项分配的字节数,因为C++中不同的数据类型在不同的机器上可能有不同的大小,但是在Java中所有的数据类型在所有机器中大小都是相同的。
5、Java的运行速度比C++慢,因为Java是半解释和半编译的。
6、C++中有多继承,Java中只有单一继承,但是java可以通过接口实现多继承
7、在C++中,数组定义时,已经分配存储空间,并且可以使用,在Java中,数组定义时只定义了数组变量,数组是不可以使用的,只有数组new之后才会创建数组,并分配存储空间。
8、C++有指针,Java没有。
● 请你说一说接口有什么限制
参考回答:
1、变量会被隐式地指定为public static final变量,并且只能是public static final变量,用private修饰会报编译错误
2、方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误)
3、接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法
● 请问Java中线程如何实现,如何实现多线程,线程安全在Java中是如何实现的,线程的工作区是哪里
参考回答:
Java中实现线程有三种方式:
1、1.继承Thread类
public class Thread extends Object implements Runnable
定义Thread类的子类,并重写Thread类的run()方法,创建子类对象(即线程对象),调用线程对象的start()方法来启动该线程。
2.实现Runnable接口
public interface Runnable
定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是该线程的执行体。创建该Runnable实现类的实例,并将此实例作为Thread的target(即构造函数中的参数)来创建Thread对象(该Thread对象才是真正的线程对象,只是该Threa
3.使用Callable和Future
创建Callable接口的实现类,并实现call()方法,该方法有返回值;创建Callable实现类的实例,使用FutureTask来包装Callable对象,并且也封装了call()方法的返回值;使用FutureTask作为Thread类的target创建并启动线程;调用FutureTask对象的get()方法返回子线程执行结束后的返回值。
如何实现多线程:
首先是继承Thread类并重写run()方法
首先是实现Runnable接口并重写run()方法
线程安全的实现:
同步代码块、同步方法、同步锁
同步代码块:同步监视器是共享资源
同步方法:同步监视器是this
同步锁:
● 请你说一说内存溢出和内存泄漏是怎么回事
参考回答:
内存溢出out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
内存泄漏可以分为四类:
1、常发性内存泄漏,发生内存泄漏的代码会被多次执行到,每次执行都会导致内存泄漏
2、偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生,
3、一次性内存泄漏,发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。
4、隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。
从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。
内存溢出常见原因:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小
解决方案:
1、修改JVM参数,直接增加内存
2、检查错误日志,查看内存溢出错误前是否有其他异常错误
3、对代码进行走查分析,找出可能发生内存溢出的位置
● 请你介绍一下HashMap,HashTable,ConcurrentHashMap
参考回答:
1、HashTable与HashMap
(1)HashTable和HashMap都实现了Map接口,但是HashTable的实现是基于Dictionary抽象类。
(2)在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在HashTable中,无论是key还是value都不能为null。
(3)HashTable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。
2、更好的选择:ConcurrentHashMap Java 5中新增了ConcurrentMap接口和它的实现类ConcurrentHashMap。 ConcurrentHashMap提供了和HashTable以及SynchronizedMap中所不同的锁机制。HashTable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能有一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。 上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。 在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是,在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator可以使用原来老的数据,而写线程也可以并发的完成改变。
● 请你说一下Hashset有什么特性,以及hashset判断存入的对象是否重复是如何比较的
参考回答:
HashSet是Set接口的实现类,因此,HashSet中的元素也是不能重复的。HashCode判断元素重复的标准时,首先计算新添加元素的hashCode值,当不重复是,则直接加入到该集合中,若发生重复,也称发生了碰撞,则进一步调用equals判断元素是否在逻辑上相同。
● 请你说一下Java的反射,你目前主要用他做什么,以及Java的泛型,他的主要作用是什么
参考回答:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。Java反射可以用来获取一个class对象或实例化一个class表示的类的对象,还可以获取构造方法,成员变量,成员方法。
java中泛型的引入主要是为了解决两个方面的问题:1.集合类型元素在运行期出现类型装换异常,增加编译时类型的检查,2. 解决的时重复代码的编写,能够复用算法。下面通过例子来说明编译器的类型检查。
● 请问类加载器你了解吗
参考回答:
类加载器就是把类加载阶段中的”通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到java虚拟机外部来实现的代码模块。
类加载器的分类:
启动类加载器、扩展类加载器、应用类加载器(系统类加载器)、用户自定义类加载器。
启动类加载器:这个类负责将存放在JAVA_HOME/lib目录或者被-Xbootclasspath参数所指定的路径中的并且是虚拟机内存中。
扩展类加载器:负责加载JAVA_HOME/lib/ext目录中或者被java.ext.dirs系统变量指定路径中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器:负责加载用户类路径上指定的类加载器,一般情况下就是程序中默认的类加载器。
● ReentranceLock 和 synchronized 有什么区别
参考回答:
1.可重入性
字面的意思就是可以再次进入的锁,synchronized其实也是可重锁,同一线程每进入一次,锁的计数器都会加一,在释放锁是计数器都会减一,只有当计数器为0 时才能释放锁
2.锁的实现
ReentrantLock是JDK实现的 Synchronized 是JVM实现
前者可以直接看到源码,后者实现难以看到
3.性能的区别
在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
4.功能的区别
便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized
当你需要时候一下三种功能是需要使用ReentrantLock
ReentranLock 可以指定公平锁还是非公平锁
(公共锁就是先等待的线程先获得锁)
实现自旋锁,通过循环调用CAS操作来实现加锁,性能比较好,避免进入内核态的线程阻塞。
提供了Condition类,可以分组唤醒需要唤醒的线程
提供能够中断等待锁的线程的机制,lock.lockInterruptibly()
具体使用场景要根据实际的业务进行分析
使用Synchronized时不需要释放锁,jvm会帮助我们做释放锁的操作