版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Java基础笔试题及答案1.简述Java中基本数据类型的分类及各自的取值范围Java基本数据类型分为4类8种:(1)整数型:byte(-128~127)、short(-32768~32767)、int(-2^31~2^31-1)、long(-2^63~2^63-1);(2)浮点型:float(约±3.40282347E+38F)、double(约±1.79769313486231570E+308);(3)字符型:char(0~65535);(4)布尔型:boolean(true/false)。注意:Java中没有无符号整数类型,所有数值类型均有符号;boolean在JVM中通常用1字节或4字节表示,但规范未明确规定具体大小。2.分析以下代码的输出结果并说明原因```javapublicclassTest{publicstaticvoidmain(String[]args){Integera=127;Integerb=127;Integerc=128;Integerd=128;System.out.println(a==b);//①System.out.println(c==d);//②inte=128;System.out.println(c==e);//③}}```输出结果:①true;②false;③true。原因:Integer通过`valueOf()`方法创建对象时,对于-128~127范围内的数值会直接从缓存(IntegerCache)中获取已存在的对象,因此a和b指向同一对象,==比较为true。超出该范围时(如128),会新建不同的Integer对象,故c和d的==比较为false。当Integer对象与基本类型int比较时,会自动拆箱为int值,因此c(128)与e(128)的数值相等,结果为true。3.说明Java中方法重载(Overload)与方法重写(Override)的区别(1)定义位置:重载发生在同一类中,重写发生在子类与父类(或接口实现类)之间;(2)参数列表:重载要求参数类型、个数或顺序至少有一个不同,与返回值无关;重写要求参数列表和返回值类型必须与父类方法完全一致(协变返回类型允许子类返回值为父类返回值的子类);(3)访问权限:重载无限制;重写时子类方法的访问权限不能小于父类方法(如父类方法为protected,子类不能为默认或private);(4)异常声明:重载无限制;重写时子类方法声明的异常不能是父类方法声明异常的父类(可更小或不声明);(5)静态绑定与动态绑定:重载在编译期通过参数类型静态绑定;重写在运行期根据对象实际类型动态绑定。4.写出以下代码的执行顺序及输出结果```javaclassGrandfather{publicGrandfather(){System.out.println("Grandfather构造");method();}publicvoidmethod(){System.out.println("Grandfathermethod");}}classFatherextendsGrandfather{privateintx=10;publicFather(){super();System.out.println("Father构造");method();}@Overridepublicvoidmethod(){System.out.println("Fathermethod,x="+x);}}classSonextendsFather{privateintx=20;publicSon(){System.out.println("Son构造");}@Overridepublicvoidmethod(){System.out.println("Sonmethod,x="+x);}}publicclassTest{publicstaticvoidmain(String[]args){newSon();}}```执行顺序及输出:(1)调用Son构造方法,隐式调用父类Father的构造方法;(2)Father构造方法调用super(),即Grandfather的构造方法;(3)Grandfather构造方法执行,输出"Grandfather构造",然后调用method()。此时实际对象是Son(多态),因此调用Son的method(),但Son的实例变量x尚未初始化(对象构造顺序为:父类静态成员→子类静态成员→父类实例成员初始化→父类构造方法→子类实例成员初始化→子类构造方法),此时Son的x为默认值0,输出"Sonmethod,x=0";(4)Grandfather构造完成,回到Father构造方法,执行实例变量x的初始化(x=10),输出"Father构造",然后调用method()。此时对象类型是Son,调用Son的method(),Son的x仍未初始化(父类构造未完成时子类实例变量未初始化),输出"Sonmethod,x=0";(5)Father构造完成,回到Son构造方法,初始化Son的x=20,输出"Son构造"。最终输出:Grandfather构造Sonmethod,x=0Father构造Sonmethod,x=0Son构造5.解释Java中finally块的执行时机及与return语句的关系finally块在以下情况执行:(1)try块正常执行完毕;(2)try块抛出异常被catch块捕获并处理;(3)try块抛出异常未被catch块捕获(此时finally执行后异常继续向上抛出)。finally块不执行的唯一情况是:JVM退出(如System.exit(0))或线程被终止。与return语句的关系:当try或catch块中存在return时,finally块会在return之前执行。若finally块中也有return语句,则会覆盖try/catch中的return结果(不推荐这种写法,易导致逻辑混乱)。例如:```javapublicstaticinttest(){try{return1;}finally{return2;}}```此方法返回2,因为finally的return覆盖了try的return。若finally中无return,但修改了返回值(如返回对象的属性),则会影响最终结果:```javapublicstaticList<Integer>test(){List<Integer>list=newArrayList<>();try{list.add(1);returnlist;}finally{list.add(2);}}```此方法返回[1,2],因为finally修改了list的内容,而list是引用类型,return返回的是引用。6.比较ArrayList、LinkedList和Vector的区别(1)底层结构:ArrayList基于动态数组(Object[]);LinkedList基于双向链表(Node节点);Vector基于动态数组(与ArrayList类似);(2)线程安全:ArrayList和LinkedList非线程安全;Vector通过synchronized修饰方法实现线程安全(性能较低);(3)随机访问:ArrayList和Vector支持O(1)时间的随机访问(通过索引);LinkedList需O(n)时间遍历;(4)插入/删除效率:ArrayList在中间插入/删除需移动元素(O(n)时间),尾部操作O(1);LinkedList在任意位置插入/删除均为O(1)(需定位到节点,定位时间O(n));(5)扩容机制:ArrayList默认初始容量10,扩容为原容量的1.5倍;Vector默认扩容为原容量的2倍(可通过构造函数指定扩容增量);(6)使用场景:频繁随机访问选ArrayList;频繁插入/删除(尤其首尾)选LinkedList;需要线程安全时优先选择CopyOnWriteArrayList(Vector已过时)。7.简述HashMap的底层实现(JDK8及以上)JDK8中HashMap的底层结构为“数组+链表+红黑树”:(1)数组(称为“桶”,table):初始容量16(DEFAULT_INITIAL_CAPACITY),负载因子0.75(DEFAULT_LOAD_FACTOR),当元素数量超过capacityloadFactor时触发扩容(resize),容量翻倍;(2)链表:当多个键的哈希值碰撞时(即哈希桶下标相同),元素以链表形式存储。链表节点为Node类型,包含hash、key、value、next属性;(3)红黑树:当链表长度≥8(TREEIFY_THRESHOLD)且数组长度≥64(MIN_TREEIFY_CAPACITY)时,链表转换为红黑树(TreeNode类型),以减少查找时间(链表查找O(n)→红黑树O(logn));当树的大小≤6(UNTREEIFY_THRESHOLD)时,红黑树退化为链表;put()方法流程:①计算key的哈希值(hash=key.hashCode()^(hash>>>16),高位参与运算减少碰撞);②若数组为空(table==null),调用resize()初始化;③计算桶下标(i=(n-1)&hash,n为数组长度);④若桶中无元素,直接插入新节点;⑤若桶中第一个元素的key与当前key相等(hash和equals均相同),覆盖value;⑥若桶中是红黑树节点,调用putTreeVal()插入树中;⑦若是链表节点,遍历链表:若找到相同key则覆盖;若遍历到末尾仍无相同key,则新增节点,若链表长度≥8,检查数组长度是否≥64,是则树化,否则扩容;⑧插入成功后,若size超过threshold(capacityloadFactor),调用resize()扩容。注意:HashMap允许null键和null值(键最多一个null,值可多个);非线程安全,多线程环境下可能导致扩容时的死循环(JDK7)或数据丢失(JDK8),应使用ConcurrentHashMap。8.说明synchronized和ReentrantLock的区别(1)锁类型:synchronized是关键字,JVM层面实现;ReentrantLock是java.util.concurrent.locks包下的类,API层面实现;(2)获取锁方式:synchronized自动获取和释放锁(作用域结束自动释放);ReentrantLock需手动调用lock()获取,unlock()释放(通常在finally块中释放);(3)公平性:synchronized是非公平锁(线程获取锁的顺序与等待顺序无关);ReentrantLock可通过构造函数指定公平锁(newReentrantLock(true)),公平锁会优先分配给等待时间最长的线程;(4)可中断性:synchronized不可中断(除非抛出异常);ReentrantLock可通过lockInterruptibly()方法实现可中断获取锁;(5)条件变量:synchronized配合wait()/notify()/notifyAll()使用,只能有一个等待队列;ReentrantLock通过Condition对象(如newCondition())支持多个等待队列(如生产者-消费者模型中可分别为生产者和消费者创建不同的Condition);(6)性能:JDK6后synchronized通过锁升级(偏向锁→轻量级锁→重量级锁)优化,性能与ReentrantLock接近,但高竞争场景下ReentrantLock性能更稳定。9.写出实现生产者-消费者模型的两种方式(要求使用wait/notify和Lock/Condition各一种)方式一:使用synchronized+wait/notify```javaimportjava.util.LinkedList;publicclassProducerConsumer{privatestaticfinalintMAX_SIZE=5;privatestaticLinkedList<Integer>queue=newLinkedList<>();publicstaticvoidmain(String[]args){Threadproducer=newThread(()->{while(true){synchronized(queue){while(queue.size()==MAX_SIZE){try{queue.wait();//队列满,生产者等待}catch(InterruptedExceptione){e.printStackTrace();}}intproduct=newjava.util.Random().nextInt(100);queue.add(product);System.out.println("生产:"+product+",队列大小:"+queue.size());queue.notifyAll();//唤醒消费者}try{Thread.sleep(500);//模拟生产耗时}catch(InterruptedExceptione){e.printStackTrace();}}});Threadconsumer=newThread(()->{while(true){synchronized(queue){while(queue.isEmpty()){try{queue.wait();//队列空,消费者等待}catch(InterruptedExceptione){e.printStackTrace();}}intproduct=queue.poll();System.out.println("消费:"+product+",队列大小:"+queue.size());queue.notifyAll();//唤醒生产者}try{Thread.sleep(800);//模拟消费耗时}catch(InterruptedExceptione){e.printStackTrace();}}});producer.start();consumer.start();}}```方式二:使用ReentrantLock+Condition```javaimportjava.util.LinkedList;importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassProducerConsumerWithLock{privatestaticfinalintMAX_SIZE=5;privatestaticLinkedList<Integer>queue=newLinkedList<>();privatestaticLocklock=newReentrantLock();privatestaticConditionnotFull=lock.newCondition();//队列未满条件privatestaticConditionnotEmpty=lock.newCondition();//队列未空条件publicstaticvoidmain(String[]args){Threadproducer=newThread(()->{while(true){lock.lock();try{while(queue.size()==MAX_SIZE){notFull.await();//队列满,等待}intproduct=newjava.util.Random().nextInt(100);queue.add(product);System.out.println("生产:"+product+",队列大小:"+queue.size());notEmpty.signal();//唤醒消费者}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}}});Threadconsumer=newThread(()->{while(true){lock.lock();try{while(queue.isEmpty()){notEmpty.await();//队列空,等待}intproduct=queue.poll();System.out.println("消费:"+product+",队列大小:"+queue.size());notFull.signal();//唤醒生产者}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}try{Thread.sleep(800);}catch(InterruptedExceptione){e.printStackTrace();}}});producer.start();consumer.start();}}```10.简述JavaIO流的分类及常用类JavaIO流按数据流向分为输入流(InputStream/Reader)和输出流(OutputStream/Writer);按处理数据类型分为字节流(处理二进制数据,以byte为单位)和字符流(处理文本数据,以char为单位,内部使用编码转换);按功能分为节点流(直接操作数据源,如FileInputStream)和处理流(包装节点流以增强功能,如BufferedInputStream)。常用字节流类:节点流:FileInputStream(文件输入)、FileOutputStream(文件输出)、ByteArrayInputStream(字节数组输入)、ByteArrayOutputStream(字节数组输出);处理流:BufferedInputStream(缓冲输入)、BufferedOutputStream(缓冲输出)、DataInputStream(基本类型输入)、DataOutputStream(基本类型输出)、ObjectInputStream(对象反序列化)、ObjectOutputStream(对象序列化)。常用字符流类:节点流:FileReader(文件字符输入)、FileWriter(文件字符输出)、CharArrayReader(字符数组输入)、CharArrayWriter(字符数组输出);处理流:BufferedReader(缓冲字符输入,支持readLine())、BufferedWriter(缓冲字符输出)、InputStreamReader(字节流转字符流,可指定编码)、OutputStreamWriter(字符流转字节流,可指定编码)、PrintWriter(格式化输出)。注意:字符流默认使用平台编码(如Windows的GBK,Linux的UTF-8),建议通过InputStreamReader/OutputStreamWriter显式指定编码(如newInputStreamReader(fileInputStream,"UTF-8"))。11.解释JVM的内存区域划分及各区域的作用JVM内存区域分为线程共享区和线程独占区:(1)线程独占区(随线程创建和销毁):程序计数器(ProgramCounterRegister):记录当前线程执行的字节码行号,线程切换时恢复执行位置。唯一不会发生内存溢出的区域;虚拟机栈(VMStack):存储栈帧(每个方法调用对应一个栈帧),包含局部变量表、操作数栈、动态链接、方法出口等信息。若线程请求的栈深度超过最大值(-Xss设置),抛出StackOverflowError;若栈扩展失败,抛出OutOfMemoryError;本地方法栈(NativeMethodStack):与虚拟机栈类似,用于本地方法(如C语言实现的方法)的调用。HotSpot虚拟机将其与虚拟机栈合并。(2)线程共享区:堆(Heap):JVM管理的最大内存区域,所有对象实例和数组在此分配。通过-XX:Xms(初始大小)和-XX:Xmx(最大大小)设置。堆内存不足时抛出OutOfMemoryError(Javaheapspace);方法区(MethodArea):存储类信息(类版本、字段、方法、接口)、常量、静态变量、即时编译器编译后的代码等。JDK7前称为“永久代”(PermanentGeneration),使用堆的一部分;JDK8后改为“元空间”(Metaspace),使用本地内存(NativeMemory),通过-XX:MaxMetaspaceSize设置上限;运行时常量池(RuntimeConstantPool):方法区的一部分,存储编译期提供的字面量(如字符串、数字)和符号引用(如类名、方法名)。JDK7后字符串常量池从方法区移至堆中;直接内存(DirectMemory):非JVM规范定义的区域,通过Unsafe类或NIO的ByteBuffer.allocateDirect()分配,不受堆内存限制,但受物理内存和-XX:MaxDirectMemorySize(默认与堆最大值相同)限制,溢出时抛出OutOfMemoryError(Directbuffermemory)。12.说明Java中类加载的过程及双亲委派机制类加载过程分为5个阶段(注意:解析阶段可能在初始化之后,称为“动态绑定”):(1)加载(Loading):通过类加载器将.class文件的二进制字节流加载到内存,提供java.lang.Class对象;(2)验证(Verification):检查字节流是否符合JVM规范(如文件格式、元数据、字节码、符号引用验证),确保安全;(3)准备(Preparation):为类的静态变量分配内存并设置初始值(如int型初始为0,引用类型初始为null)。常量(staticfinal)在此阶段直接赋值为编译期常量值;(4)解析(Resolution):将符号引用(如“java.lang.Object”)替换为直接引用(内存地址);(5)初始化(Initialization):执行类构造器<clinit>()方法(由编译器自动收集类中所有静态变量的赋值动作和静态代码块合并提供),按顺序执行。双亲委派机制:类加载器收到类加载请求时,不会立即自己加载,而是先委托给父类加载器(非继承关系,而是组合关系),父类加载器再继续向上委托,直到启动类加载器(BootstrapClassLoader)。若父类加载器无法加载(未找到对应.class文件),则子类加载器尝试自己加载。作用:(1)避免重复加载:确保同一个类只被加载一次;(2)保证安全:防止用户自定义的类覆盖核心API(如自定义java.lang.String会被父类加载器加载为rt.jar中的String,不会使用用户自定义的版本)。打破双亲委派的场景:(1)热部署/热替换:如OSGi框架中,每个模块有独立的类加载器,可自定义加载顺序;(2)JNDI、JDBC等需要调用SPI实现类的场景:父类加载器(如启动类加载器)无法加载子类路径的实现类,需通过线程上下文类加载器(ThreadContextClassLoader)反向委托;(3)自定义类加载器:某些框架(如Tomcat)为隔离不同应用的类,会自定义类加载器并覆盖loadClass()方法。13.简述Java垃圾回收(GC)的常见算法及HotSpot的常用收集器常见GC算法:(1)标记-清除(Mark-Sweep):标记不可达对象,然后清除。缺点:产生内存碎片,可能导致大对象无法分配;(2)复制(Copying):将内存分为大小相等的两块,每次只使用一块,回收时将存活对象复制到另一块,然后清除当前块。缺点:可用内存减半,适用于存活对象少的区域(如新生代);(3)标记-整理(Mark-Compact):标记存活对象后,将其向内存一端移动,然后清除边界外的内存。避免碎片,适用于存活对象多的区域(如老年代);(4)分代收集(GenerationalCollection):根据对象存活周期分为新生代(Eden区+两个Survivor区)和老年代。新生代使用复制算法(Eden:Survivor1:Survivor2=8:1:1,每次使用Eden+一个Survivor,回收后存活对象复制到另一个Survivor或老年代);老年代使用标记-清除或标记-整理算法。HotSpot常用收集器(JDK8及以上):(1)Serial收集器:单线程,新生代使用复制算法,老年代使用标记-整理。适用于客户端模式或单核环境;(2)ParNew收集器:Serial的多线程版本,与CMS配合使用(JDK9后不再推荐);(3)ParallelScavenge(ParallelYoung):多线程,新生代复制算法,目标是达到可控制的吞吐量(吞吐量=运行用户代码时间/(用户代码时间+GC时间)),适用于后台计算;(4)ParallelOld:ParallelScavenge的老年代版本,标记-整理算法,与ParallelScavenge配合实现“吞吐量优先”;(5)CMS(ConcurrentMarkSweep):老年代收集器,目标是低停顿。流程:初始标记→并发标记→重新标记→并发清除。缺点:占用CPU资源,无法处理浮动垃圾(并发标记期间新产生的垃圾),标记-清除算法产生碎片;(6)G1(Garbage-First):JDK9默认收集器,跨新生代和老年代,将内存划分为多个Region,优先回收垃圾多的Region(GarbageFirst)。流程:初始标记→并发标记→最终标记→筛选回收。支持预测停顿时间,适用于大内存、低延迟场景;(7)ZGC(JDK11+):基于Region和读屏障,支持TB级内存,停顿时间不超过10ms,适用于超大型应用。14.分析以下代码可能存在的问题及改进方法```javapublicclassUnsafeCache{privateMap<String,Object>cache=newHashMap<>();publicObjectget(Stringkey){Objectvalue=cache.get(key);if(value==null){value=loadFromDB(key);//从数据库加载cache.put(key,value);}returnvalue;}privateObjectloadFromDB(Stringkey){//模拟数据库查询returnnewObject();}}```存在的问题:(1)线程不安全:多线程并发调用get()时,可能出现多个线程同时检测到value为null,导致重复加载数据库并多次put(),浪费资源;(2)可能的空指针:若loadFromDB返回null(数据库无对应数据),cache中会存储null,后续线程获取到null后可能再次加载(需根据业务需求决定是否缓存null);(3)HashMap非线程安全:多线程put()可能导致数据丢失或死循环(JDK7)。改进方法:使用ConcurrentHashMap替代HashMap,并通过putIfAbsent()原子操作避免重复加载:```javaimportjava.util.concurrent.ConcurrentHashMap;publicclassSafeCache{privateConcurrentHashMap<String,Object>cache=newConcurrentHashMap<>();publicObjectget(Stringkey){Objectvalue
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论