面试题集——Java之Java SE

最近忙着复习,之前好多东西都忘了,也有一些知道,但到真正要自己说的时候又不一定能流利说出口,所以,在牛客网看到了一位师兄提出的面试可能问题,就试着按自己知道的回答回答,不知道的那就赶紧查查

一、J2SE部分
1.8种基本类型:
boolean:1位,true或false(实际上大小并没有明确指定)但在多个boolean的情况下时,编译器(有些)会将多个变量安排在一个字节里,一个的话就是一字节咯
byte:8位,-128 - 127
short:16位,-32768 - 32767
int:32位
long:64位
float:32位
double:64位
char:16位,无符号。Unicode编码的话两字节可以一汉字。UTF8的话,3-4字节。UTF8是变长编码,1-6字节。具体规则这里就不说了,有兴趣的可以查查
基本类型属于Class的一个子集
判断一个对象是否是基本类型
Class oClass=object.getClass();
if(oClass.isPrimitive())return true;
int.class.isPrimitive()返回的就是true
   扩展:
Void类型:既非基本数据类型,也不是引用类型,是一种伪类型,是一种返回类型
 java.lang.Void它是一个不可实例化的占位符类,保存着Java关键词void的Class对象
但是,void.class.isPrimitive()返回的也是true,从这个角度来看,也算是基本类型了


2.switch
Java7之前,switch只支持byte、char、short、int、枚举类型(Enum)
Java7添加了对String的支持(但是switch(空指针异常,实际上是取得变量hascode)和case(编译不通过,case必须常量表达式)中变量不能为null)


3.Object的方法:
(1).默认构造函数,可忽略
(2).registerNatives:本地方法注册器,可忽略--------------------------------------------private static native
(3).getClass:获得该对象类型的运行时Class对象------------------------native
(4).hashCode:获得对象的默认哈希值(默认是内存地址)-----------------------------------native
(5).equals:比较两个对象是否相等,默认比较的是地址是否相等。String、Date等库中引用类型大多数已经重写了(当然也重写了hashCode和toString方法)
(6).toString:返回类型的描述信息,自定义类型若不重写,返回的是一个地址(其实就是默认的hashCode的值的十六进制)
(7).wait([int mili][,int noans])---------------------没有参数和两个参数的,其实调用的就是一个参数的那个
(8).notify
(9).notifyAll:以上,三个方法是线程同步用的--------------------且都是native
(10).clone:克隆当前对象,若不重写,为浅克隆(如果类的数据都是基本类型,浅克隆=深克隆)--------------native
(11).finalize:清理对象之前调用,而且只调用一次(可能在真正调用这个方法时候,这个对象又活了,之后清理就不会调用了),哪怕调用了,GC也不一定马上清理


4.equals和==
equals是Object的一个方法,而Object是Java的根类,所以每个Java对象都有equals方法,但是你若不覆盖(重写),默认就是Object的方法
默认的equals和==作用一样,比较的是否是同一块内存(是否是同一个对象,这样说更恰当?)


5.四种引用
强引用:平常建立的引用变量都是这种。
软引用:SoftReference,用于对内存敏感的高速缓存,内存足够不清理,不足清理
弱引用:WeakReference,一次性短暂使用?不管内存足不足,下一次肯定清理你
虚引用:PhantomReference,用于跟踪对象,必须与引用队列联合使用;跟没有一样
ReferenceQueue<String> queue = new ReferenceQueue<String>();
         PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);


6.hashCode()方法的作用:
要知道它的作用,首先要说说Java中的集合,我们就举例说说List和Set
都知道List是有序的,元素可以重复的,Set是无序的,不可重复,但如何判断元素是否重复呢
这就要用到equals方法,但每增加一个元素就要比较检查一次,当元素个数很多呢,后添加的元素就要比较很多次了
于是,Java采用了哈希表的原理:将数据以特定的算法直接指定到一个地址上,这就是hashCode
另外,可能hashCode值相同,但equals值不同(hash冲突),equals相同hash相同(除非你自定义的类重写了equals没重写hashCode,但这明显是不提倡的)


7.ArrayList、Vector、LinkedList
(1).ArrayList、Vector是顺序存储的(内部是一个数组,实际上是可以称为动态数组——内存不足,申请一个新的更大数组,复制),而LinkedList是链式存储的(内部是一个双向链表)
(2).ArrayList、LinkedList是线程不安全的,Vector是线程安全的


8.String、StringBuffer、StringBuilder
(1).String是不可变类,StringBuffer、StringBuilder是可变类
(2).StringBuffer是线程安全的,而StringBuilder不是,所以单线程下,StringBuilder执行速度快


9.List、Set、Map、Stack、Queue作用及特点
(0).Stack是类,其他的都是接口
(1).List/Set/Queue,都是Collection,Map接口,与Collection同等级,Stack是Vector子类,Vector实现了List接口
(2).List:动态数组,可自动扩增的数组
(3).Set:集合,这个集合,是数学上的集合的概念,既不能有重复值
(4).Map:键值对,键不可重复(另外说一下,Java中基本上所有Set的实现类底层用的都是对应的Map实例实现的)
(5).Stack:栈,先进后出。至于有什么作用,一般都是根据他们特点而用到他们,比如说,Stack,JVM的线程栈就是用它实现的,一个方法,一个栈帧
(6).Queue:队列, 先进先出。图的广度遍历,线程池里面的排队队列-------平常的队列LinkedList、阻塞队列、优先级队列
add--remove:失败,抛出异常
offer--poll:失败,返回false
element--peek:取出队头元素,不移除前段元素,只是取出值,但element失败抛异常,peek返回false


10.数组与集合类(这里主要是说List),最主要区别:长度不可变与可变
11.HashMap和Hashtable区别---------这里注意Hashtable的写法,t不是大写的
1.继承不同
2.Hashtable是线程安全的,HashMap线程不安全
3.HashMap键可为null(当然,同样的只能有一个),Hashtable不可以
4.hash不同,每次的扩增也不同。HashMap初始容量16(0.75),每次增加当前容量的一半,哈希值是重新计算的,Hashtable初始容量11(0.75),每次增加一倍+1


12.ConcurrentHashMap:其实就是将HashMap分段(里面有个Segment类),每段独立操作(同步),不在同一个段的就可以同时操作了(Java8之前)
Java8,采用的是第一个插入的时候用的是CAS,以后冲突采用的是,锁定桶第一个对象,进行分段同步,更细化


13.TreeMap、HashMap、LinkedHashMap
TreeMap:红黑树,放入的键的类型必须实现comparable接口,或给TreeMap一个排序器
HashMap:数组+链表+红黑树,初始默认容量16(0.75),扩容每次翻倍
LinkedHashMap:双链表(插入顺序)+链表+红黑树,有头结点,尾节点;是HashMap的子类,它保留插入的顺序
按访问顺序(调用get方法),默认是按插入顺序(保存)


14.Collection和Collections
(1).Collection是个接口,集合类顶级接口,提供了诸如:size()/add/addAll/remove/removeAll/toArray/contains等方法

Collections是对Collection子类的工具类(私有的构造函数,无法实例化)
Collections提供的方法:fill值、求最大值最小值,排序、混排、反转、顺移,交换;包装非同步集合为同步集合


15.Java对象三个特征
(1).封装:属性封装,行为开放。外面不可直接访问对象的属性(对象自身信息,通过private关键字限定属性),需要通过对象的方法才能访问,安全性
可以通过方法限定外界读取,和检测写入,保证数据有效性,提高代码的健壮性、安全性
(2).继承:继承可以让继承类获得被继承类的所有共有的、受保护的成员变量和方法(不包括构造函数)
目的:增强代码的复用性,提供代码的质量
实现:extends关键字,只能单继承
当然子类也可以自定义自己的属性和方法,默认Java类都是继承Object类的(编译器判断该类是否有父类,没有添加Object为其父类,有些是JVM做的这些工作)
(3).多态:允许不同类的对象对同一个消息做出不同反应或同一个对象的一个行为接受到不同消息做出不同反应
动态多态:三个必要条件:1.要有继承;2.要有重写;3.父类引用指向子类对象
要说明多态:首先得知道静态类型、动态类型
静态类型:编译时候确定;动态类型:运行时确定


静态绑定:编译期间,确定哪个方法,这个时候编译器只能调用(绑定)静态类型里面声明了的方法(没有编译不通过),具体运行还要另看
动态绑定:运行时确定调用那个方法;Java默认这种方法
//静态多态:方法重载,个人认为不是啊
动态多态:方法重写
好处:
(1).可替换性:对已存在的代码具有可替换性
(2).可扩充性:增加新的子类,不影响已存在的子类
(3).接口性:接口用的就是多态的特点
(4).灵活性:灵活多变
(5).简化性:
原理:就是继承+重写+动态绑定


六个原则:
总原则:开闭原则:对修改严格限制,对扩展大力支持
降低程序耦合性,当已有软件需要增加新功能,不要对原有做修改
对于个事物抽象化
1.单一职责原则:一个类只负责一项职责,只关心一件事,不要存在多个导致类变更
降低类的复杂度,一个类负责一项工作,逻辑简单多了;提高可读性,系统维护性

2.里氏替换原则:所有基类出现的地方都能让其子类替换,而系统不会有所擦觉
3.接口隔离原则:接口细分,用多个小接口替换一个大接口,其实这是单一职责在接口上面的体现
4.依赖倒置原则:依赖于抽象,不要依赖于与具体;面向接口编程,而不是面向实现
5.迪米特法则:最少知道原则,一个实体尽量少的与其他实体有关系
6.合成复用原则:尽量使用合成聚合的方式,而不是继承


16.重载与重写
1.重载在一个类里面,重写是在继承链上
2.重载:方法名相同,参数列表不同;重写:都一样,返回值只要可以兼容就可以


17.静态类和非静态类:主要区别是静态类不能实例化
静态类:仅包含静态成员、无法实例化(不能包含实例构造函数)、密封


18.异常
Throwable
--Error:堆溢出、栈溢出、永久区溢出、直接内存溢出
--Exception
--RuntimeException:逻辑错误,程序人员的错误
错误的类型转换,数组越界访问。访问空指针、零除、NoSuchElement、UnkownTypeException
--IOException:非RuntimeException
打开一个不存在的文件、文件尾部读取数据(EOFException)、根据给定的字符串查找不存在的Class

19.Thread和Runnable
实现多线程:1.继承Thread类;2.实现Runnable接口
线程最终其实都是有Thread的start方法启动,看源码知道,Thread有个内置属性,Runnable r;
在启动线程的时候会先判断这个属性是否为空(Runnable方法启动就是通过向Thread构造函数传递一个Runnable对象实现的)
区别:Runnable能共享数据,而Thread想实现同样功能,更麻烦;还有依照设计原则——合成复用原则


20.线程同步:synchronized、lock、ReentrantLock
synchronized其实就是一个简化了的lock;synchronized只能有一个竞争条件,而lock可以有多个
lock可以中断锁,可以设计超时,轮询,synchronized不能,只能一直等待
synchronized在JVM层次实现的,可以自动释放锁,而lock是代码实现的,一定要手动释放锁。所以出现异常,lock一定要把释放锁放在finally里面
竞争激烈的时候用lock

21.方法锁、对象锁、类锁
其实就是作用范围不同
方法锁:锁住这个方法,同时只能有一个线程进入该方法
对象锁:想进入这个对象作用的区域,必须获得这个对象的锁;注意,不同对象同一个方法是可以被多线程访问的这个时候
类锁:该类所有方法同一时间只能有一个线程访问
在类的某个方法加上static synchronized或synchronized(obj.class){}


22.ThreadLocal:线程局部变量,initialValue/set/get/remove四个方法
不是用来解决多线程共享问题的,他是线程内部的私有对象,其他线程无法访问
实现机制:每一个线程都有一个自己的ThreadLocalMap类对象
ThreadLocal类里面有一个map存放的就是<Thread,variable>
延迟加载,其实就是每个线程里面放了一个副本,放在了这个static final 全局变量ThreadLocal对象里


23.ThreadPool:
线程池,实现原理:里面有一个排队队列(阻塞队列),这个是任务队列,还有一组活动的线程,有任务,有空闲的线程,就发给它执行,满了,就等待
具体实现:ExecutorService service=Executors.newFixedThreadPool(2);
好处:减少创建关闭线程的开销,控制线程数量,过多的是线程性价比并不高
线程池就是用来管理线程的,适用于大量短小线程,线程无同步问题,因为进入线程池就有线程池管理了


24.ArrayBlockingQueue:有界的阻塞队列(缓冲区),一旦创建了就不能改变其容量
阻塞队列:试图向已满的队列添加数据---阻塞;试图向空的队列取数据,阻塞


25.wait和sleep
1.wait是Object的方法;sleep是Thread静态方法
2.wait是要配合synchronized配合使用的(一定放在synchronized里面),调用后线程阻塞,被阻塞的线程只能由notify或notifyAll方法唤醒
3.sleep只是将线程睡眠,让其他线程先执行,等待时间到自然苏醒,重新回到可运行状态等待调度
4.最重要区别,sleep睡眠的时候,是不会释放已持有的锁的,而wait释放锁


36.for(;;)和foreach()
foreach只能遍历读,不能修改;效率最低;内部实现还是用的iterator,只不过不能修改,不能remove


37.Java IO和NIO
IO流:一组有顺序的,有起点终点的字符集合,是对数据传输的总称或抽象
字符流和字节流
最基本的就是比特流,以8比特组成一字节,每次读8比特形成一个特定意义的字节,就是字节流
而字符流就是根据不同编码,将多个字节形成一个字符,就是字符流
1.IO:面向流,阻塞;NIO:面向缓冲,非阻塞
2.面向流:每次从流读取一个或多个字节,直至所有字节,不能移动;向移动,就得借助缓冲区
3.阻塞:read和write方法会阻塞,知道读到(满)数据或完全写入
//4.选择器:允许一个单线程监视多个输入通道


38.Java反射原理:
可以在程序运行期间动态的创建类的实例,获得任意对象的类的信息,比如该类的构造器、方法信息。属性信息。静态域等等
说到反射,就要说到Java的类加载机制
当我们要实例某各类的对象,首先要加载这个类,如果已经加载(向上查看加载器的缓存是否已经加载),然后就通过new来创建实例
若没有,就要加载这个类先,自上而下试图加载
反射就是调用的Class.forName()或ClassLoader.loadClass()方法获得该类的类对象(Class对象),然后通过Class的newInstance方法实力对象。也可以获得这个类的构造器、方法信息。属性信息。静态域等等,通过相应的方法



39.XML加载方式:DOM、SAX、PULL
DOM:一次全部加载,对于小的还可以,文件大了,速度慢;符合日常思维,简单容易上手
支持删除、修改、重排列等多种操作
浪费空间和时间
适用于一旦文档解析之后还要多次访问这些数据,硬件资源充足
先把XML读到内存中,然后再用DOM API访问树形结构,并获取数据
事实上DOM解析是在SAX解析器上基础建立的
SAX:基于事件驱动,可以立即开始,当某个条件满足也可以停止解析,速度快,不必存储在内存,读取到文档开始和结束标签就会回调一个事件。
很难同一时间访问同一文档不同数据,编程难
基于事件:事件源、事件处理器
事件源:XMLReader,通过parser()方法解析XML



40.JNI
public class JNITest{
static{
system.loadLibrary("test");//系统加载dll或OS文件,不需要加后缀,系统自己判断
}
public native static void setXxx(int i);//方法要加native修饰
public native static void getXxx();
}


3.使用javac编译该文件,获得.class文件
使用javah -classpath JNITest 生成响应的.h文件
4.C/C++
新建工程->win32 DLL,选择空程序,编写test.cpp文件
#include <windows.h>  
#include "JNITest.h"  //注意这里
int i = 0;  
//JNIEXPORT JNICALL 都是JNI关键字  jintJava与本地操作系统交流;JNIEnv jclass固定要有的两个参数;注意Java的参数和这里的参数类型转换
JNIEXPORT jint JNICALL Java_test_get (JNIEnv *, jclass)  //函数名:Java_test_get  Java关键字+全限定类名+方法名
{  
return i;  
}  
JNIEXPORT void JNICALL Java_test_set (JNIEnv *, jclass, jint j)  
{  
i = j;  
}  
int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)   
{   
return TRUE ;   

必须要有DllMain函数,且其必须返回TRUE,否则系统报错
编译,生成dll文件,哦,还要注意要加上相应的jni.h文件
\jdk\include\jni.h  
\jdk\include\win32\jawt_md.h  
\jdk\include\win32\jni_md.h
上面三个是在jdk下,复制到Microsoft Visual Studio的安装目录下的\VC\include文件夹下
最后:
将dll文件放在.class文件目录下


在具体实现的时候,我们只关心两个函数原型
JNIEXPORT jint JNICALL Java_TestJNI_get (JNIEnv *, jclass); 和
JNIEXPORT void JNICALL Java_TestJNI_set (JNIEnv *, jclass, jint);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,
就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,
我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值