Java面试题基础篇
八种基本数据类型的大小、默认值以及他们的封装类
基本类型 | 大小(字节) | 默认值 | 封装类 | 取值范围 |
---|---|---|---|---|
byte | 1 | 0 | Byte | -27至27-1 |
short | 2 | 0 | Short | -215至215-1 |
int | 4 | 0 | Integer | -231至231-1 |
long | 8 | 0L | Long | -263至263-1 |
float | 4 | 0.0f | Float | – |
double | 8 | 0.0d | Double | – |
boolean | - | false | Boolean | – |
char | 2 | \u0000 | Character | – |
布尔类型所占字节大小有以下三种说法:
- 1个bit
boolean类型的值只有true和false两种逻辑值,在编译后会使用1和0来表示,这两个数在内存中只需要1位(bit)即可存储,位是计算机最小的存储单位。
- 1个字节
虽然编译后1和0只需占用1位空间,但计算机处理数据的最小单位是1个字节,1个字节等于8位,实际存储的空间是:用1个字节的最低位存储,其他7位用0填补,如果值是true的话则存储的二进制为:0000 0001,如果是false的话则存储的二进制为:0000 0000。
- 4个字节
来源是《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。
总结:很显然第三种说法是更准确的,由此可得出如果使用boolean类型声明一个变量的话,一个boolean占四个字节,如果使用boolean声明一个数组类型,那么数组的每个元素占一个字节。
switch中能否使用String做参数
在JDK7之前,switch只支持byte,short,char,int或是其对应的包装类及Enum类型,从JDK1.7开始switch就支持了String做参数。
ps:jdk1.7并没有新的指令来处理switch string,而是通过调用switch中string.hashCode,将string转换为int从而进行判断。
equals与==的区别
- 对于基本数据类型,使用“=="比较值是否相等。
- 对于引用数据类型,使用equals()和“==”效果是一样的,两者比较的都是对象在内存中的存放地址(确切的说,是堆内存地址)
- 对于String、Integer、Date等覆盖了equals()方法的类型,“==”比较的是存放的内存地址。而equals()的结果则由覆盖后的代码决定。
Object类有哪些公用方法
Object是所有类的父类,任何类都默认继承Object。
- clone 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则会抛出
CloneNotSupportedException
异常。 - equals 在Object中与==是一样的效果,子类一般需要重写该方法,例如String类。
- hashCode 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
- getClass final方法,获得运行时类型。
- wait 使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。 wait() 方法一直等待,直到获得锁或者被中断。 wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。调用该方法后当前线程进入睡眠状态,直到以下事件发生1、其他线程调用了该对象的notify方法。 2、其他线程调用了该对象的notifyAll方法。 3、其他线程调用了interrupt中断该线程。 4、时间间隔到了。 5、此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
- notify 唤醒在该对象上等待的某个线程。
- notifyAll 唤醒在该对象上等待的所有线程。
- toString 转成字符串,一般子类都有重写,否则打印句柄。
Java的四种引用,强弱软虚,用到的场景
- 强引用
最最普通的一种引用方式,比如String name = "Tony"
,变量name就是字符串"Tony"的强引用,只要强引用存在,垃圾回收期就不会回收这个对象。 - 软引用
用于描述还有用,但是非必须的对象,如果内存足够,不回收,如果内存不足,则会被回收。一般用于实现内存敏感的高速缓存,软引用可以和引用队列ReferenceQueue联合使用,如果软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。 - 弱引用
弱引用和软引用大致相同,弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 - 虚引用
就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
ArrayList、LinkedList、Vector的区别
List的三个子类的特点:
ArrayList
- 底层结构是数组,查询快,增删慢。
- 底层线程不安全,效率高。
Vector
- 底层结构是数组,查询快,增删慢。
- 线程安全,效率低。
- Vector相对ArrayList查询慢(线程安全的)
- Vector相对LinkedList增删慢(数组结构)
LinkedList
- 底层数据结构是链表,查询慢,增删快。
- 线程不安全,效率高。
Vector和ArrayList的区别
- Vector是线程安全的,效率低。
- ArrayList是线程不安全的,效率高。
- 共同点:
底层数据结构都是数组实现的,查询快,增删慢。
ArrayList和LinkedList的区别
- ArrayList底层是数组结构,查询和修改快。
- LinkedList底层是链表结构,增删快,查询和修改比较慢。
- 共同点:
都是线程不安全的。
List有三个子类使用
- 查询多,用ArrayList。
- 增删多,用LinkedList。
- 如果查询和增删都多那就用ArrayList。
String、StringBuffer、StringBuilder的区别
String:适用于少量的字符串操作情况。
StringBuilder:适用于“单线程”下在字符缓冲区进行大量操作的情况。
StringBuffer:适用于“多线程”下在字符缓冲区进行大量操作的情况。
StringBuilder是线程不安全的,而StringBuffer是线程安全的。
这三个类的区别主要在于两个方面,运行速度和线程安全方面的区别。首先说运行速度,StringBuilder > StringBuffer > String。
因为String是字符串常量,而StringBuffer和StringBuilder都是字符串变量,String对象一旦创建是不可以修改的,但StringBuffer和StringBuilder都是变量是可以修改的,所以String是三者中最慢的。
在线程安全上,StringBuiler是线程不安全的,StringBuffer反之,如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法带有synchronized,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作,所以如果要进行的操作是多线程的,那就用StringBuffer,如果是在单线程的情况下,还是建议使用速度更快的StringBuilder。
接口和抽象类的区别
比较 | 抽象类 | 接口 |
---|---|---|
默认方法 | 抽象类可以有默认的实现 | JDK1.8之前,接口中不存在方法的实现 |
实现方法 | 子类使用extends关键字来继承抽象类,如果子类不是抽象类,子类需要实现抽象类中所有的抽象方法 | 子类使用implements实现接口,需要提供接口中所有声明的实现 |
构造器 | 抽象类中可以有构造器 | 接口中不能有构造器 |
和正常类的区别 | 抽象类不能被实例化 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default等修饰 | 接口默认是public,不能使用其他访问修饰符 |
多继承 | 一个子类只能存在一个父类 | 一个类可以实现多个接口 |
添加新方法 | 向抽象类中添加新方法,可以提供默认的实现,因此可以不必修改子类现有的实现 | 如果往接口中添加新方法,则子类中必须实现该方法 |
面向对象的三大特征
- 封装
封装符合面向对象设计原则的第一条:单一性原则,一个类把自己该做的事情封装起来,而不是暴露给其他类去处理,当内部的逻辑发生变化时,外部调用不用因此而修改,他们只调用开放的接口,而不用去关心内部的实现
- 继承
继承是类与类的一种关系,是一种“is a”的关系。比如“狗”继承“动物”,这里动物类是狗类的父类或者基类,狗类是动物类的子类或者派生类。子类拥有父类的所有属性和方法(除了private修饰的属性不能拥有)从而实现了实现代码的复用
- 多态
Java中的多态是建立在继承的基础之上的,多态是同一个行为具有多个不同表现形式或形态的能力。比如说“宠物”这个对象,它就有很多不同的表达或实现,有小猫,小狗等等,我们到宠物店去跟服务员说“我要一只宠物”,那么服务员给我小猫,小狗都可以,因为他们都是宠物,所以我们就说“宠物”这个对象具有多态性。
Java程序设计六大原则
单一职责
指一个类的功能要单一,不能包罗万象,高内聚,低耦合。
开闭原则
指一个模块在扩展性方面应该是开放的,在更改性方面应该是关闭的。为达到目的需要对系统进行抽象化设计(关键)。
替换原则
子类应当可以替换父类,并可以出现在父类能够出现的任何位置。
依赖原则
具体依赖抽象,上层依赖下层
接口分离原则
模块间要通过抽象接口隔开,而不是通过具体的类强耦合起来。
迪米特原则
一个对象应当尽可能少的去了解其他对象,尽可能的做到“老死不相往来”,核心就是两个类/两个系统之间知道的东西越少越好!狭义的迪米特法则中也有相关的概念——调用转发,尽量使用一个第三者的朋友来进行通信如果必须要通信的话。
以上原则都是为了一个目的——“高内聚,低耦合”
Java中有没有GOTO
Java中的保留字,目前还没有在Java中使用。
has-a与is-a的区别
is-a表示的是属于的关系。比如小狗属于一种动物(继承关系)。
has-a表示组合,包含关系。比如小狗包含有牙齿,舌头等组件。
ClassLoader如何加载class
JVM里有多个类加载器,每个类加载器可以负责加载特定位置的类,例如,bootstrap类加载负责加载jre/lib/rt.jar中的类,我们平时用的jdk中的类都位于rt.jar中。extclassloader负责加载jar/lib/ext/*.jar中的类,appclassloader负责classpath指定的目录或jar中的类。除了bootstrap之外,其他的类加载器本身也都是java类,它们的父类是ClassLoader。
hashCode方法的作用
hashcode这个方法是用来鉴定2个对象是否相等的。
equals方法和hashCode方法这2个方法都是用来判断2个对象是否相等的,但是他们是有区别的。
一般来讲,equals这个方法是给用户调用的,如果你想判断2个对象是否相等,你可以重写equals方法,然后在代码中调用,就可以判断他们是否相等了。简单来讲,equals方法主要是用来判断从表面上看或者从内容上看,2个对象是不是相等。举个例子,有个学生类,属性只有姓名和性别,那么我们可以认为只要姓名和性别相等,那么就说这2个对象是相等的。
hashcode方法一般用户不会去调用,比如在hashmap中,由于key是不可以重复的,他在判断key是不是重复的时候就判断了hashcode这个方法,而且也用到了equals方法。这里不可以重复是说equals和hashcode只要有一个不等就可以了!所以简单来讲,hashcode相当于是一个对象的编码,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比较起来不直观。我们一般在覆盖equals的同时也要覆盖hashcode,让他们的逻辑一致。举个例子,还是刚刚的例子,如果姓名和性别相等就算2个对象相等的话,那么hashcode的方法也要返回姓名的hashcode值加上性别的hashcode值,这样从逻辑上,他们就一致了。
要从物理上判断2个对象是否相等,用==就可以了,如果两个对象的物理(内存)地址相等,那么这两个对象肯定就是同一个对象。
Java中堆和栈有什么不同
每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其他线程是不可见的。而堆是所有线程共享的一片内存区域。对象都是在堆里面创建,为了提升效率会从堆中弄一个缓存到自己的栈。
堆:(对象)
引用类型的变量,其内存分配在堆上或者常量池(字符串常量、基本数据类型常量),需要通过new等方式来创建。
堆内存主要作用是存放运行时创建(new)的对象。
栈:(基本数据类型变量、对象的引用变量)
基本数据类型的变量以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放。
线程安全是什么?线程不安全是什么?
就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问。
什么叫线程安全:
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的,反之就是线程不安全的。
线程安全问题都是由全局变量及静态变量引起的,若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。