1、final、 finally、 finalize 三个关键字的区别是(ABC)
A)final 是修饰符(关键字)可以修饰类、方法、变量
B)finally在异常处理的时候,提供finally块执行任何清除操作
C)finalize是方法名,在垃圾收集器将对象从内存中清除出去之前做
D)finally 和 finalize 一样都是用异常处理的方法
分析:本题主要考察final,finally,finalize的区别,在平常的学习中,这三个关键字看似相似,但是作用,应用场景等都有所区别。final用于修饰变量,方法,类,final修饰类:当用final修饰一个类时,表明这个类不能被继承;final修饰方法:使用final方法的原因有两个,第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。final修饰变量:修饰变量是final用得最多的地方,对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。finally是异常处理语句结构的一部分,表示总是执行,无论是否发生异常,使用finally可以维护对象的内部状态,并可以清理非内存资源。finalize 是一个方法,属于 Object 类的一个方法,而Object 类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用 System 的 gc()方法的时候,由垃圾回收器调用 finalize(),回收垃圾。
2、当编译运行下面程序会发生什么结果(D)
public class Test extends Thread { public static void main(String[] args) { Test t = new Test(); t.run(); } public void start() { for (int i = 0; i < 10; i++) { System.out.println(i); } } }
A、编译错误,指明 run 方法没有定义 B、 运行错误,指明 run 方法没有定义
C、编译通过并输出 0 到 9 D、编译通过但无输出
分析:run方法里是线程执行体,一般新创建线程会重写该方法,但要注意区分start方法与run方法,start方法是线程的方法,真正启动一个线程的方法,对象直接调用run方法相当于调用普通的方法,不能启动线程。此题中的start方法重写了Thread类的start方法,对象t调用了run方法,未调用start方法,不会输出0到9,Test类未重写Thread类的run方法,则会直接调用Thread的run方法,执行run方法,如下,故编译通过但无输出。
public void run() { if (target != null) { target.run(); } }
3、关于 sleep()和 wait(),以下叙述错误的一项是:(D)
A) sleep 是线程类(Thread)的方法, wait 是 Object 类的方法; 。
B) sleep 不释放对象锁, wait 放弃对象锁。
C) sleep 暂停线程、但监控状态仍然保持,结束后会自动恢复
D) wait 后进入等待锁定池,只有针对此对象发出 notify 方法后获得对象锁进入运行状态
分析:
本题考查sleep方法和wait方法,不过在平常的学习中还要注意yield方法。
sleep和yield是线程的静态方法,而wait是Object类的方法;sleep和yield不释放锁标志,释放cpu资源,wait会释放锁标志,也会释放cpu资源;sleep有参数,在指定时间内使线程暂停,yield无参数,sleep方法必须有异常捕获。对于wait方法注意:调用wait方法后,会释放锁,进入对象等待池,直到调用noyify或notifyAll方法,会唤醒线程进入到锁等待池,获得竞争锁的资格。
4、给定一个 java 程序的 main 方法的代码片段如下:假如 d 目录下不存在 abc.txt 文件,现运行该程序,下面的结果正确的是(C):
try { PrintWriter out = new PrintWriter(new FileOutputStream("d:/abc.txt")); String name = "chen"; out.print(name); } catch (FileNotFoundException e) { System.out.println("文件没有发现"); }
A、将在控制台上打印: "文件没有发现! "
B、正常运行,但没有生成文件 abc.txt
C、运行后生成 abc.txt,但该文件中可能无内容
D、运行后生成 abc.txt,该文件内容为: chen
分析:
PrintWriter有自动flush的功能,但是必须在构造函数中传入PrintWriter log = new PrintWriter(fw,true); true才会自动冲刷数据。
上面那段代码没有true,所以程序结束了也不会将缓存区的东西刷进文件中,但PrintWriter的第2个参数为true时,只是使 println、printf 或 format 方法将刷新输出缓冲区,并不能作用print方法。还有就是FileOutputStream的创建不依赖于文件是否存在,如果不存在,则在打开之前创建它。
5、集合框架中,要实现对集合里的元素进行自定义排序,要实现哪个接口(D)
A) Cloneable
B) Runnable
C) Serializable
D) Comparator
分析:
Cloneable接口是实现复制的,Runable接口用于创建线程,Serilizable接口用于实现序列化。要实现排序功能,要实现Comparator接口,同时注意区分Comparator和Comparable的区别。Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
6、transient 和 volatile 是 java 的关键字吗?它们有什么作用?
答:这两者是 Java 关键字
transient 用于对象序列化;transient 只能用来修饰字段,在对象的序列化中,标记为transient 的字段不会被持久化;
volatile 用于线程同步;volatile 也是变量修饰符,只能用来修饰变量;
volatile关键字可以实现可见性和有序性,volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变 化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
此处还要扩展复习synchronized,现简要介绍synchronized和volatile的区别:
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
7、Java 中堆和栈有什么区别?
(1)JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。
(2)对于所占空间大小,栈通常都比堆小,
(3)与线程的关系不同,栈是线程私有的,堆被整个 JVM 的所有线程共享。
(4)生命周期不同,栈的生命周期与线程一致,堆的生命周期与JVM进程一致。
(5)栈:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时, Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理的。
8、启动 3 个线程打印递增的数字, 线程 1 先打印 1,2,3,4,5, 然后是线程 2 打印 6,7,8,9,10, 然后是线程 3 打印 11,12,13,14,15. 接着再由线程 1 打印 16,17,18,19,20….以此类推, 直到打印到 75.
分析:本题的关键是如何判断每到5个数字更换线程执行,此处找到数字与线程id的关系:通过(num/5%3)+1是否等于id进行判断,满足条件则进行输出数字,不满足则等待,让下一个线程执行(通过wait和notifyAll方法)。
private int id;//线程 private static int num = 1;//数字 public PrintNumThread(int id){ this.id = id; } @Override public void run() { synchronized (PrintNumThread.class){ while (num <= 75){ if((num/5%3)+1 == id) { System.out.print("线程" + id + ":"); for (int i = 0; i < 5; i++) { System.out.print(num++ + " "); } System.out.println(); PrintNumThread.class.notifyAll(); }else{ try { PrintNumThread.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
输出结果:
线程1:1 2 3 4 5
线程2:6 7 8 9 10
线程3:11 12 13 14 15
线程1:16 17 18 19 20
线程2:21 22 23 24 25
线程3:26 27 28 29 30
线程1:31 32 33 34 35
线程2:36 37 38 39 40
线程3:41 42 43 44 45
线程1:46 47 48 49 50
线程2:51 52 53 54 55
线程3:56 57 58 59 60
线程1:61 62 63 64 65
线程2:66 67 68 69 70
线程3:71 72 73 74 75
9、针对 1,2,2,3,4,5 这 6 个数字,写一个函数,打印出所有不同的排列,例如 512234,215432 等,要求‘4’不能在第三位,‘3’和‘5’不能相连。
分析:本题要求这6个数字的全排列,同时满足两个要求,整体思路:先确定第一位的数字,相当于求另外5位的全排列,再进一步,确定第二位的数字,求另外4位的全排列,以此类推,当确定了前5位的数字。最后一位的数字也就确定了,递归执行,不断缩小范围,注意递归终止的条件。
public class Test { static HashSet<String> myset = new HashSet<>();//去重 public static void main(String[] args) { Integer[] arr = new Integer[]{1,2,2,3,4,5}; Test.allPerm(arr,0,""); Integer c = 1; for(String a: myset){ System.out.println((c++)+":"+a); } } public static void allPerm(Integer[] arr, int count, String result){ if(count == 6){ if(result.indexOf('4') != 2 && !result.contains("35") && !result.contains("53")){ myset.add(result); } }else { for(int i = 0;i < arr.length;i++){ if(arr[i] != 0){ int tempCount = count; String temp = result; temp += arr[i]+""; Integer[] brr = arr.clone(); brr[i] = 0; allPerm(brr, ++tempCount, temp); } } } } }
输出结果:
1:322145
2:512234
3:225134
......
196:451232
197:341225
198:452321