首页 > 编程 > Java > 正文

JavaSE面试题集锦

2019-11-06 08:39:18
字体:
来源:转载
供稿:网友
一、IO流1.文件切割器public class SplitFileDemo {PRivate static final int SIZE = 1024 * 1024;/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {File file = new File("c://aa.mp3");splitFile_2(file);}private static void splitFile_2(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。245FileOutputStream fos = null;int len = 0;int count = 1;/** 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。* 这个信息为了进行描述,使用键值对的方式。用到了properties对象**/Properties prop = new Properties();File dir = new File("c://partfiles");if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));fos.write(buf, 0, len);fos.close();}// 将被切割文件的信息保存到prop集合中。prop.setProperty("partcount", count + "");prop.setProperty("filename", file.getName());fos = new FileOutputStream(new File(dir, count + ".properties"));// 将prop集合中的数据存储到文件中。prop.store(fos, "save file info");fos.close();fis.close();}public static void splitFile(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。FileOutputStream fos = null;int len = 0;int count = 1;File dir = new File("c://partfiles");if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));fos.write(buf, 0, len);}fos.close();fis.close();}}2. 定义功能,获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示。并不要在运行程序。public class PropertiesTest {/*** @param args* @throws IOException* @throws Exception*/public static void main(String[] args) throws IOException {getAppCount();}public static void getAppCount() throws IOException {// 将配置文件封装成File对象。File confile = new File("count.properties");if (!confile.exists()) {confile.createNewFile();}FileInputStream fis = new FileInputStream(confile);Properties prop = new Properties();prop.load(fis);// 从集合中通过键获取次数。String value = prop.getProperty("time");// 定义计数器。记录获取到的次数。int count = 0;if (value != null) {count = Integer.parseInt(value);if (count >= 5) {// System.out.println("使用次数已到,请注册,给钱! ");// return;throw new RuntimeException("使用次数已到,请注册,给钱! ");}}count++;// 将改变后的次数重新存储到集合中。prop.setProperty("time", count + "");FileOutputStream fos = new FileOutputStream(confile);prop.store(fos, "");fos.close();fis.close();}}3. 对指定目录进行所有内容的列出(包含子目录中的内容)public class FileTest {/*** @param args*/public static void main(String[] args) {File dir = new File("e://demodir");listAll(dir, 0);}public static void listAll(File dir, int level) {System.out.println(getSpace(level) + dir.getName());二、多线程1. java 多线程同步的五种方法http://www.codeceo.com/article/java-multi-thread-sync.html2. 什么是ThreadLocal变量?https://my.oschina.net/lichhao/blog/1113623. 一个线程运行时发生异常会怎样?这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。4. sleep() 和 wait() 有什么区别?sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。对于wait的讲解一定要配合例子代码来说明,才显得自己真明白。5. 请阐述一下你对JAVA多线程中“锁”的概念的理解。6. 简述synchronized和java.util.concurrent.locks.Lock的异同 ?主要相同点:Lock能完成synchronized所实现的所有功能主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。7. 线程的基本概念、线程的基本状态以及状态之间的关系线程是进程中某个单一顺序的控制流,是程序执行流的最小单位。线程由线程ID、当前指令指针、寄存器集合和堆栈组成。线程是进程的一个实体,是被系统调度和分配的基本单位,线程与同一进程中的其他线程共享进程的全部资源。      线程有五种基本状态:新生状态,就绪状态,运行状态,阻塞状态,死亡状态。状态间关系如下图:  http://www.cnblogs.com/Rosanna/p/3581835.html8. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?分几种情况:    1.其他方法前是否加了synchronized关键字,如果没加,则能。    2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。    3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。    4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。 来源: http://blog.csdn.net/nyistzp/article/details/141275479. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。同步和异步是相对于多线程来说的。        同步可防止并发, 主要出于数据安全的考虑。如果数据将在线程间共享,例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。        异步允许并发,当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。来源: http://blog.csdn.net/yuguiyang1990/article/details/1533982710. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?第一种:步骤:1,定义一个类继承Thread类。2,覆盖Thread类中的run方法。3,直接创建Thread的子类对象创建线程。4,调用start方法开启线程并调用线程的任务run方法执行。  new Thread(){}.start();这表示调用Thread子类对象的run方法,new Thread(){}表示一个Thread的匿名子类的实例对象,子类加上run方法后的代码如下:new Thread(){    public void run(){    }}.start();第二种:1,定义类实现Runnable接口。2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。4,调用线程对象的start方法开启线程。  new Thread(new Runnable(){}).start();这表示调用Thread对象接受的Runnable对象的run方法,new Runnable(){}表示一个Runnable的匿名子类的实例对象,runnable的子类加上run方法后的代码如下:new Thread(new Runnable(){    public void run(){    }}).start();从java5开始,还有如下一些线程池创建多线程的方式:ExecutorService pool = Executors.newFixedThreadPool(3)for(int i=0;i<10;i++){pool.execute(new Runable(){public void run(){}});}Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});有两种实现方法,分别使用new Thread()和new Thread(runnable)形式,第一种直接调用thread的run方法,所以,我们往往使用Thread子类,即new SubThread()。第二种调用runnable的run方法。有两种实现方法,分别是继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。三、基础语法1. 给出下面的二叉树先序、中序、后序遍历的序列? 先序:    A    B    D    E    G    I    H    C    F 中序:    D    B    G    I    E    H    A    C    F后序:    D    I    G    H    E    B    F    C    A2. 为什么在重写了equals()方法之后也必须重写hashCode()方法?object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true (2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode()来源: http://www.cnblogs.com/happyPawpaw/p/3744971.html3. abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?1、abstract与static(what)abstract:用来声明抽象方法,抽象方法没有方法体,不能被直接调用,必须在子类overriding后才能使用static:用来声明静态方法,静态方法可以被类及其对象调用(how)static与abstract不能同时使用(why)用static声明方法表明这个方法在不生成类的实例时可直接被类调用,而abstract方法不能被调用,两者矛盾。2、abstract与native(what)native:用来声明本地方法,该方法的实现由非java 语言实现,比如C。一般用于java与外环境交互,或与操作系统交互(how)native 可以与所有其它的java 标识符连用,但是abstract除外。(why)因为native 暗示这些方法是有实现体的,只不过这些实现体是非java 的,但是abstract却显然的指明这些方法无实现体。3、abstract与synchronized(what)synchronized:用于防止多个线程同时调用一个对象的该方法,与static连用可防止多个线程同时调用一个类的该方法(how)abstract与synchronized不能同时使用(why)从synchronized的功能也可以看出,用synchronized的前提是该方法可以被直接调用,显然和abstract连用来源: http://zhoujiyu.blog.51cto.com/675686/4289994. 能不能自己写个类,也叫java.lang.String?http://blog.csdn.net/tang9140/article/details/427384335. heap和stack有什么区别。栈内存:在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 栈内存主要存放的是基本类型类型的数据 如、( int, short, long, byte, float, double, boolean, char) 和对象句柄。并没有有String基本类型、在栈内存的数据的大小及生存周期是必须确定的、其优点是寄存速度快、、栈数据可以共享、缺点是数据固定、不够灵活。栈的共享:1. String a = "abc";2. String b = "abc";3. System.out.println(a==b);结果为true 这就说明了a b其实指向同一个值注意,我们这里并不用a.equals(b);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,a与b是否都指向了同一个对象。结果说明,JVM创建了两个引用a和b,但只创建了一个对象,而且两个引用都指向了这个对象。首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有abc这个值,如果没找到,就将abc存放进来,然后将a指向abc。接着处理String b = "abc";在创建完b的引用变量后,因为在栈中已经有abc这个值,便将b直接指向abc。这样,就出现了a与b同时指向abc特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=abcd;那么,b不会等于abcd,还是等于abc。在编译器内部,遇到a= abcd;时,它就会重新搜索栈中是否有abcd的字面值,如果没有,重新开辟地址存放abcd的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。堆内存:堆内存用来存放所有new 创建的对象和 数组的数据、1. String a = new String ("abc");2. String b = "abc";3. System.out.println(a==b);  //False4. String a = new String ("abc");5. String b = new String ("abc");6. System.out.println(a==b);  //False创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。6.try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?你中招了……话说当年某天才还开玩笑说用这个例子做笔试题拿去面试的,后来不知道有没有真用。《深入Java虚拟机》里有详细说明,先执行return,把结果缓存起来,然后再执行finally,最终返回缓存的结果。7.下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";对于如下代码:String s1 = "a";String s2 = s1 + "b";String s3 = "a" + "b";System.out.println(s2 == "ab");System.out.println(s3 == "ab");第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,String s ="a" + "b" + "c" + "d";System.out.println(s== "abcd");最终打印的结果应该为true。来源: http://blog.sina.com.cn/s/blog_8f329b7b0101nkh2.html8. 数组有没有length()这个方法? String有没有length()这个方法?数组没有length()这个方法,有length的属性。String有有length()这个方法9. Anonymous Inner Class(匿名内部类)是否可以 extends(继承)其它类,是否可以 implements(实现)interface(接口)?1、什么是匿名内部类?内部类,存在于另一个类内部的类,而匿名内部类,顾名思义,就是没有名字的内部类。2、为什么需要匿名内部类?每个inner class都能够各自继承某一实现类(implementation)。因此,inner class不受限于outer class是否已继承自某一实现类。如果少了inner class所提供的“继承自多个具象(concrete)或抽象(abstract)类”的能力,设计上和编程上的某些问题会变得十分棘手。所以,从某个角度来看inner class,你可以说它是多重继承问题的完整解决方案。interface能够解决其中一部分问题,但inner classes才能有效而实际地允许“多重实现继承(multiple implementation)”。也就是说,inner classes实际上允许你继承多个non-interface。从这个层面上使用内部类时一般都是通过其父类或继承的接口来进行实例化和初始化的,如3中所示,这时内部类的名字是多余的,所以就使用了匿名内部类3、怎么实现匿名内部类?匿名内部类的实现非常简单,如有一接口Interface A{public void method();}这时你可以写一个如下的语句得到接口A的实现类的对象A a=new A(){ public void method(){System.out.println("hehe");} };而其中的{ public void method(){System.out.println("hehe");} };就是定义了一个匿名内部类4、问题答案以上可以看出匿名内部类在实现时必须借助一个类或一个接口,若从这个层次上讲它是可以继承其他类也可以实现接口的,但若是通过extends或implements关键字那是不可能的来源: http://blog.csdn.net/fhm727/article/details/522000310. 什么是内部类?Static Nested Class 和 Inner Class 的不同。Inner Class(内部类)定义在类中的类。 (一般是JAVA的说法)Nested Class(嵌套类)是静态(static)内部类。(一般是C++的说法)静态内部类:1 创建一个 static 内部类的对象,不需要一个外部类对象2 不能从一个 static 内部类的一个对象访问一个外部类对象Java 内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。内部类:就是在一个类的内部定义的类,A.非静态内部类中不能定义静态成员(静态对象是默认加载,那么静态内部类应该先于外部类被加载到内存中)当内部类中如果有静态成员时,该内部类也必须是静态的。而静态内部类只能访问外部类中的静态成员。 会出现两种情况:1,内部类静态,但是方法没静态。该方法需要被对象调用执行。Outer.Inner in = new Outer.Inner();//产生了一个静态内部类对象。in.show();2,内部类静态,而且方法也是静态的。静态内部类随着对象的加载就已经存在了,静态方法随着内部类的加载也存在了。这时是不需要对象的。Outer1.Inner.show();B.内部类可以直接访问外部类中的成员变量非静态的内部类之所以可以直接访问外部类中的成员,那是因为内部类中都持有一个外部类对象引用:外部类名.this.变量名静态内部类之所以可以直接访问外部类中的静态成员,其实持有的是外部类名。C.内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中,如下所示:public class Outer{int out_x = 0;public void method(){Inner1 inner1 = new Inner1();class Inner2{ //在方法体内部定义的内部类void method(){out_x = 3;}}Inner2 inner2 = new Inner2();}public class Inner1{ //在方法体外面定义的内部类}在方法体外面定义的内部类的访问类型可以是 public,protected,默认的,private等4种类型,它们决定这个内部类的定义对其他类是否可见;对于这种情况,我们也可以在外面创建内部类的实例对象,一定要先创建外部类的实例对象,然后用这个外部类的实例对象去创建内部类的实例对象,代码如下:Outer outer = new Outer();Outer.Inner1 inner1 = outer.new Innner1();在方法内部定义的内部类前面不能有访问类型修饰符,但前面可以使用 final 或 abstract 修饰符。这种内部类对其他类是不可见的其他类无法引用这种内部类,但是这种内部类创建的实例对象可以传递给其他类访问。这种内部类必须是先定义,后使用,即内部类的定义代码必须出现在使用该类之前,这与方法中的局部变量必须先定义后使用的道理也是一样的。这种内部类可以访问方法体中的局部变量,但是,该局部变量前必须加 final 修饰符。在方法体内部还可以采用如下语法来创建一种匿名内部类,没有名字的内部类,即定义某一接口或类的子类的同时,还创建了该子类的实例对象,无需为该子类定义名称:public class Outer{public void start(){new Thread(new Runable(){public void run(){};}).start();}}Static Nested Class,它不再具有内部类的特性,从狭义上讲,它不是内部类。Static Nested Class 与普通类在运行时的行为和功能上没有什么区别,只是在编程引用时的语法上有一些差别:1.它可以定义成public、protected、默认的、private 等多种类型,而普通类只能定义成 public 和默认 的这两种类型。2.在外面引用Static Nested Class 类的名称为“外部类名.内部类名”。3.在外面不需要创建外部类的实例对象,就可以直接创建 Static Nested Class,例如,假设 Inner 是定义在 Outer 类中的 Static Nested Class,那么可以使用如下语句创建 Inner 类:Outer.Inner inner = new Outer.Inner();4.由于static Nested Class 不依赖于外部类的实例对象,所以,static Nested Class 能访问外部类的非 static 成员变量。5.当在外部类中访问Static Nested Class 时,可以直接使用 Static Nested Class 的名字,而不需要加上外部类的名字了,在 Static Nested Class 中也可以直接引用外部类的 static 的成员变量,不需要加上外部类的名字。在静态方法中定义的内部类也是 Static Nested Class,这时候不能在类前面加 static 关键字,静态方法中的 Static Nested Class 与普通方法中的内部类的应用方式很相似,它除了可以直接访问外部类中的 static 的成员变量,还可以访问静态方法中的局部变量,但是,该局部变量前必须加 final 修饰符。备注:内部类的总体方面的特点:两个地方可以定义,可以访问外部类的成员变量,不能定义静态成员,这是大的特点。然后再说一些细节方面的知识,例如,几种定义方式的语法区别,静态内部类,以及匿名内部类。11. 什么是java序列化?如何实现java序列化?或者请解释Serializable接口的作用。将一个 java 对象变成字节流的形式传出去或者从一个字节流中恢复成一个 java 对象序列化是将对象状态转换为可保持或传输的格式的过程。java 中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过 socket 进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。一般而言,要使得一个类可以序列化,只需简单实现 java.io.Serializable 接口即可。序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。    序列化的实现:将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream )来构造一个 ObjectOutputStream (对象流)对象,接着,使用 ObjectOutputStream 对象的 writeObject(Object obj) 方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。12. 运行时异常与一般异常有何异同运行时异常和非运行时异常(1)运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。当出现 RuntimeException 的时候,我们可以不处理。当出现这样的异常时,总是由虚拟机接管。比如:我们从来没有人去处理过 NullPointerException 异常,它就是运行时异常,并且这种异常还是最常见的异常之一。出现运行时异常后,如果没有捕获处理这个异常(即没有 catch),系统会把异常一直往上层抛,一直到最上层,如果是多线程就由 Thread.run() 抛出,如果是单线程就被 main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。运行时异常是 Exception 的子类,也有一般异常的特点,是可以被 catch 块处理的。只不过往往我们不对他处理罢了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。如果不想终止,则必须捕获所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。(2)非运行时异常是 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。如 IOException、SQLException 等以及用户自定义的 Exception 异常。对于这种异常,JAVA 编译器强制要求我们必需对出现的这些异常进行 catch 并处理,否则程序就不能编译通过。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆 catch 块去处理可能的异常。13. Java 的接口和 C++ 的虚类的相同和不同处C++虚类相当与 java 里面的抽象类,与接口的不同之处如下:1、一个子类只能继承一个抽象类(虚类),但能实现多个接口;2、一个抽象类可以有构造方法,接口没有构造方法;3、一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有声明;4、一个抽象类可以是 public、private、protected、default,接口只有 public;5、一个抽象类中的方法可以是 public、private、protected、default,接口中的方法只能是 public 和 default.相同之处:都不能实例化。补充说明:接口是一类特殊的抽象类,是更抽象的抽象类,你可能这样理解。抽象类是一个不完整的类,接口只是定义了一些功能。打个比方,用抽象类和接口分别描述“猪”,抽象类就是在一般的类之前加 abstract ,说:猪能用四肢跑,猪还能怎么怎么,接口的话只能说,猪会跑,用什么跑就是子类的事啦。14. swtich 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上在 switch(expr1)中,expr1只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是 int 基本类型或 Integer 包装类型,由于,byte,short,char 都可以隐含转换为 int,所以,这些类型以及这些类型的包装类型也是可以的。显然,long 和 String 类型都不符合 switch 的语法规定,并且不能被隐式转换成 int 类型,所以,它们不能作用于 switch 语句中。Java 7中,switch的参数可以是String类型了15. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。-------------------------------------------------------------在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。但重要的是要区分参数是如何传递的,这才是该节选的意图。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。  Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。   按 值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。按引用传递意味 着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数的值,调用代码中的原始值也随之改变。如果 函数修改了该参数的地址,调用代码中的原始值不会改变.   当传递给函数的参数不是引用时,传递的都是该值的一个副本(按值传递)。区别在于引用。在 C++ 中当传递给函数的参数是引用时,您传递的就是这个引用,或者内存地址(按引用传递)。在 Java 应用程序中,当对象引用是传递给方法的一个参数时,您传递的是该引用的一个副本(按值传递),而不是引用本身。   Java 应用程序按值传递参数(引用类型或基本类型),其实都是传递他们的一份拷贝.而不是数据本身.(不是像 C++ 中那样对原始值进行操作。)来源: http://xinklabi.iteye.com/blog/199610516. 两个对象值相同 (x.equals(y) == true) ,但却可有不同的 hashcode ,这句话对不对?对。如果对象要保存在HashSet或HashMap中,它们的equals相等,那么,它们的hashcode值就必须相等。如果不是要保存在HashSet或HashMap,则与hashcode没有什么关系了,这时候hashcode不等是可以的,例如arrayList存储的对象就不用实现hashcode,当然,我们没有理由不实现,通常都会去实现的。四、集合、容器、泛型等1. 集合框架中的泛型有什么优点?Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。2. 你所知道的集合类都有哪些?主要方法?线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。Collection├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap└WeakHashMapCollection接口Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:Iterator it = collection.iterator(); // 获得一个迭代子while(it.hasNext()) {Object obj = it.next(); // 得到下一个元素}由Collection接口派生的两个接口是List和Set。List接口List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。LinkedList类LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(...));ArrayList类ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。和LinkedList一样,ArrayList也是非同步的(unsynchronized)。Vector类Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。Stack 类Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。Set接口Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。Map接口请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。Hashtable类Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:Hashtable numbers = new Hashtable();numbers.put(“one”, new Integer(1));numbers.put(“two”, new Integer(2));numbers.put(“three”, new Integer(3));要取出一个数,比如2,用相应的key:Integer n = (Integer)numbers.get(“two”);System.out.println(“two = ” + n);由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。Hashtable是同步的。HashMap类HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。WeakHashMap类WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。总结如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。3. Comparable和Comparator接口是什么?Comparable和Comparator接口有何区别?http://www.cnblogs.com/skywang12345/p/3324788.html4. ArrayList 和 Vector 的区别 ,HashMap 和 Hashtable的区别这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与hashset没有任何关系,但为了说清楚ArrayList与Vector的功能,我们使用对比方式,更有利于说明问题)。接着才说ArrayList与Vector的区别,这主要包括两个方面:.(1)同步性:Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。备注:对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。(2)数据增长:ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。HashMap和Hashtable的区别(条理上还需要整理,也是先说相同点,再说不同点)HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。就HashMap与HashTable主要从三方面来说。一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的三.值:只有HashMap可以让你将空值作为一个表的条目的key或value五、网络编程1. 使用TCP建立交互方式客户端1. public class ClientDemo2 {2. /**3. * @param args4. * @throws IOException5. * @throws UnknownHostException6. */7. public static void main(String[] args) throws UnknownHostException, IOException {8. // 客户端发数据到服务端9. /*10. * Tcp传输,客户端建立的过程。 1,创建tcp客户端socket服务。使用的是Socket对象。11. * 建议该对象一创建就明确目的地。要连接的主机。 2,如果连接建立成功,说明数据传输通道已建立。 该通道就是socket流 ,是底层建立好的。12. * 既然是流,说明这里既有输入,又有输出。 想要输入或者输出流对象,可以找Socket来获取。13. * 可以通过getOutputStream(),和getInputStream()来获取两个字节流。 3,使用输出流,将数据写出。14. * 4,关闭资源。15. */16. Socket socket = new Socket("192.168.1.100", 10002);17. OutputStream out = socket.getOutputStream();18. out.write("tcp演示:哥们又来了!".getBytes());19. // 读取服务端返回的数据,使用socket读取流。20. InputStream in = socket.getInputStream();21. byte[] buf = new byte[1024];22. int len = in.read(buf);23. String text = new String(buf, 0, len);24. System.out.println(text);25. // 关闭资源。26. socket.close();27. }28. }服务端1. public class ServerDemo2 {2. /**3. * @param args4. * @throws IOException5. */6. public static void main(String[] args) throws IOException {7. // 服务端接收客户端发送过来的数据,并打印在控制台上。8. /*9. * 建立tcp服务端的思路: 1,创建服务端socket服务。通过ServerSocket对象。10. * 2,服务端必须对外提供一个端口,否则客户端无法连接。 3,获取连接过来的客户端对象。11. * 4,通过客户端对象获取socket流读取客户端发来的数据 并打印在控制台上。 5,关闭资源。关客户端,关服务端。12. */13. // 1创建服务端对象。14. ServerSocket ss = new ServerSocket(10002);15. // 2,获取连接过来的客户端对象。16. Socket s = ss.accept();17. String ip = s.getInetAddress().getHostAddress();18. // 3,通过socket对象获取输入流,要读取客户端发来的数据19. InputStream in = s.getInputStream();20. byte[] buf = new byte[1024];21. int len = in.read(buf);22. String text = new String(buf, 0, len);23. System.out.println(ip + ":" + text);24. // 使用客户端socket对象的输出流给客户端返回数据25. OutputStream out = s.getOutputStream();26. out.write("收到".getBytes());27. s.close();28. ss.close();29. }30. }2. 聊天程序(多线程)发送端1. public class Send implements Runnable {2. private DatagramSocket ds;3. 4. public Send(DatagramSocket ds) {5. this.ds = ds;6. }7. 8. @Override9. public void run() {10. try {11. BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));12. String line = null;13. while ((line = bufr.readLine()) != null) {14. byte[] buf = line.getBytes();15. DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.255"), 10001);16. ds.send(dp);17. if ("886".equals(line))18. break;19. }20. ds.close();21. } catch (Exception e) {22. }23. }24. }接收端1. public class Rece implements Runnable {2. private DatagramSocket ds;3. 4. public Rece(DatagramSocket ds) {5. this.ds = ds;6. }7. 8. @Override9. public void run() {10. try {11. while (true) {12. // 2,创建数据包。13. byte[] buf = new byte[1024];14. DatagramPacket dp = new DatagramPacket(buf, buf.length);15. // 3,使用接收方法将数据存储到数据包中。16. ds.receive(dp);// 阻塞式的。17. // 4,通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。18. String ip = dp.getAddress().getHostAddress();19. int port = dp.getPort();20. String text = new String(dp.getData(), 0, dp.getLength());21. System.out.println(ip + "::" + text);22. if (text.equals("886")) {23. System.out.println(ip + "....退出聊天室");24. }25. }26. } catch (Exception e) {27. }28. }29. }开启发送和接收两个线程开始运行聊天  1. public class ChatDemo {2. /**3. * @param args4. * @throws IOException5. */6. public static void main(String[] args) throws IOException {7. DatagramSocket send = new DatagramSocket();8. DatagramSocket rece = new DatagramSocket(10001);9. new Thread(new Send(send)).start();10. new Thread(new Rece(rece)).start();11. }12. }3. 使用UDP建立信息接收端1. public class UDPReceDemo {2. /**3. * @param args4. * @throws IOException5. */6. public static void main(String[] args) throws IOException {7. System.out.println("接收端启动......");8. /*9. * 建立UDP接收端的思路。 1,建立udp socket服务,因为是要接收数据,必须要明确一个端口号。10. * 2,创建数据包,用于存储接收到的数据。方便用数据包对象的方法解析这些数据.11. * 3,使用socket服务的receive方法将接收的数据存储到数据包中。 4,通过数据包的方法解析数据包中的数据。 5,关闭资源12. */13. // 1,建立udp socket服务。14. DatagramSocket ds = new DatagramSocket(10000);15. // 2,创建数据包。16. byte[] buf = new byte[1024];17. DatagramPacket dp = new DatagramPacket(buf, buf.length);18. // 3,使用接收方法将数据存储到数据包中。19. ds.receive(dp);// 阻塞式的。20. // 4,通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。21. String ip = dp.getAddress().getHostAddress();22. int port = dp.getPort();23. String text = new String(dp.getData(), 0, dp.getLength());24. System.out.println(ip + ":" + port + ":" + text);25. // 5,关闭资源。26. ds.close();27. }28. }4. 使用UDP建立信息发送端1. public class UDPSendDemo {2.    /**3.     * @param args4.     * @throws IOException5.     */6.    public static void main(String[] args) throws IOException {7.        System.out.println("发送端启动......");8.        /*9.         * 创建UDP传输的发送端。 思路: 1,建立udp的socket服务。 2,将要发送的数据封装到数据包中。10.         * 3,通过udp的socket服务将数据包发送出去。 4,关闭socket服务。11.         */12.        // 1,udpsocket服务。使用DatagramSocket对象。13.        DatagramSocket ds = new DatagramSocket(8888);14.        // 2,将要发送的数据封装到数据包中。15.        String str = "udp传输演示:哥们来了! ";16.        // 使用DatagramPacket将数据封装到的该对象包中。17.        byte[] buf = str.getBytes();18.        DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.100"), 10000);19.        // 3,通过udp的socket服务将数据包发送出去。使用send方法。20.        ds.send(dp);21.        // 4,关闭资源。22.        ds.close();23.    }24. }
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表