java基础面试题

参考:http://blog.csdn.net/jackfrued/article/details/44921941java

说未经容许不转载,我只好参考了。程序员

1.面向对象的特征有哪些方面?面试

  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
  • 继承:继承是从已有类获得继承信息建立新类的过程。提供继承信息的类称为父类(超累,基类);获得继承信息的类被称为子类(派生类)。继承让变换中的软件系统有了必定的延续性。同时继承也是封装程序中可变因素的重要手段。
  • 封装:一般认为封装是把数据和操做数据的方法绑定起来,对数据的访问只能经过已定义的接口。面向对象的本质就是将现实世界描绘成一系列的彻底自治、封闭的对象。咱们在类中编写的方法就是对实现细节的一种封装;咱们编写一个类就是对数据和数据操做的封装。能够说,封装就是隐藏一块儿能够隐藏的东西,只向外界提供最简单的编程接口。
  • 多态性:多态性是容许不一样子类型对象对同一消息做出不一样的响应。简单的说就是用一样的对象引用调用一样的方法可是作了不一样的事情。多态性分为编译时多态性和运行时多态性。若是将对象的方法是为对象向外界提供的服务,那么运行时的多态性能够解释为:调用不一样的子类对象替换父类对象。方法重载(overload)实现的是编译时多态性(也成为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态须要作两件事:1方法重写(子类继承父类并重写父类中已有的或抽象的方法)2对象造型(用父类引用子类对象)

2.访问修饰符权限算法

权限分为:当前类,同包,子类,其余包编程

public都可;protected其余包不可;default同包下的能够;private只有本身能够。设计模式

3.String是基本数据类型吗api

答:不是。java中8中基本类型:byte,short,int,long,float,double,char,boolean;除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)。数组

 4.float f=3.4缓存

错误,默认是double的,须要强转,或者f=3.4f;安全

5.int和integer

为了将基本数据类型当作对象操做,Integer为包装类(wrapper class)。

Integer缓存为-128到127.因此,这个范围内的Integer对象是同一个,==为true。其余为false。

6.&和&&

&连接的操做符都要计算。&&是短路运算,即当前面表达式有错误就中止计算。

7.解释内存中的栈(stack)、堆(heap)、和静态区(static area)的 用法

答:一般咱们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而经过new关键字和构造器建立的对象放在堆空间;程序中的字面量(literal)如直接书写的100、“hello"和常量都是放在静态区中。栈空间操做起来最快但栈很小,一般大量的对象都是放在堆空间,理论上整个内存没有被其余进程使用的空间甚至磁盘上的虚拟内存均可以当作堆空间来使用。

String str = new String("hello");

  上面,str放栈,用new出来的字符串对象放堆上,而“hello”这个字面量放在静态区。

java6开始使用“逃逸分析”的技术,能够将一些局部对象放在栈上提高对象操做性能。

8.switch是否能够用在byte,long,String?

答:java5前只能够:byte、short、char、int。5后增长enum,7后增长String.

9.最有效率的方法计算2乘以8

答:2<<3(左移3至关于乘以2的3次方,右移3至关于除以2的3次方)

补充:咱们为编写的类重写hashCode方法时,可能会看到以下所示的代码,其实咱们不太理解为何要使用这样的乘法运算来产生哈希码(散列码),并且为何这个数是个素数,为何一般选择31这个数?前两个问题的答案你能够本身百度一下,选择31是由于能够用移位和减法运算来代替乘法,从而获得更好的性能。说到这里你可能已经想到了:31 * num 等价于(num << 5) - num,左移5位至关于乘以2的5次方再减去自身就至关于乘以31,如今的VM都能自动完成这个优化。

10.数组有没有length()方法,String有没有length()方法?

答:数组没有length()方法,有length属性。String有length()方法。js中字符串是length属性。

 11.构造器constructor是否能够override?

答:构造器不能被继承,所以不能被重写,但能够被重载。

12.两个对象值相同(x.equals(y)==true),但却能够有不一样的hash code,这句话对不对?

答:不对。equals的hashcode必须相同。

13.是否能够继承String类

答:String类是final类,不能够被继承。继承String是个错误的行为,应该用关联关系(Has-A)和依赖关系(Use A)而不是继承关系(Is-A).

14.当一个对象被看成参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里究竟是值传递仍是引用传递? 
答:是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例做为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性能够在被调用过程当中被改变,但对对象引用的改变是不会影响到调用者的。

15.String==

@Test
    public void str_c(){
        String a = "hehe";
        String b = "he"+"he";
        String c = new String("hehe");
        String d = new String("hehe");

    System.out.println(a==b);//true
    System.out.println(a==c);//false
    System.out.println(a==a.intern());//true
    System.out.println(c==d);//false
    }

16.重载(Overload)和重写(Override)的区别。重载的方法可否根据返回类型进行区分? 
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,然后者实现的是运行时的多态性。重载发生在一个类中,同名的方法若是有不一样的参数列表(参数类型不一样、参数个数不一样或者两者都不一样)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

2二、char 型变量中能不能存贮一个中文汉字,为何? 
答:char类型能够存储一个中文汉字,由于Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的惟一方法),一个char类型占2个字节(16比特),因此放一个中文是没问题的。

23.抽象类(abstract class)和接口(interface)有什么异同?

答:抽象类和接口都不能实例化,但能够定义抽象类和接口类型的引用。一个类若是继承了某个抽象类或者实现了某个接口都须要对其中的抽象方法所有进行实现,不然该类仍然须要声明为抽象类。接口比抽象类更加抽象,由于抽象类中能够定义构造器,能够有抽象方法和具体方法,而接口中不能定义构造器并且其中的方法所有都是抽象方法。抽象类中的成员能够是privae,默认,protected,public,而接口中的成员变量所有是public。抽象类中能够定义成员变量,而接口中定义的成员白嬢实际上都是常量。有抽象方法的类必须被声明为抽象类,抽象类未必有抽象方法。

2四、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不一样? 
答:Static Nested Class是被声明为静态(static)的内部类,它能够不依赖于外部类实例被实例化。而一般的内部类须要在外部类实例化后才能实例化。

2五、Java 中会存在内存泄漏吗,请简单描述。 
答:理论上Java由于有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被普遍使用于服务器端编程的一个重要缘由);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,所以也会致使内存泄露的发生。例如Hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,若是不及时关闭(close)或清空(flush)一级缓存就可能致使内存泄露。下面例子中的代码也会致使内存泄露。

import java.util.Arrays;
import java.util.EmptyStackException;

public class MyStack<T> {
    private T[] elements;
    private int size = 0;

    private static final int INIT_CAPACITY = 16;

    public MyStack() {
        elements = (T[]) new Object[INIT_CAPACITY];
    }

    public void push(T elem) {
        ensureCapacity();
        elements[size++] = elem;
    }

    public T pop() {
        if(size == 0) 
            throw new EmptyStackException();
        return elements[--size];
    }

    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

 上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下彷佛没有什么明显的问题,它甚至能够经过你编写的各类单元测试。然而其中的pop方法却存在内存泄露的问题,当咱们用pop方法弹出栈中的对象时,该对象不会被看成垃圾回收,即便使用栈的程序再也不引用这些对象,由于栈内部维护着对这些对象的过时引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无心识的对象保持。若是一个对象引用被无心识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其余对象,即便这样的对象只有少数几个,也可能会致使不少的对象被排除在垃圾回收以外,从而对性能形成重大影响,极端状况下会引起Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至形成OutOfMemoryError。

 2六、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰? 
答:都不能。抽象方法须要子类重写,而静态的方法是没法被重写的,所以两者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,所以也是相互矛盾的。

2七、阐述静态变量和实例变量的区别。 
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类无论建立多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,须要先建立对象而后经过对象才能访问到它。静态变量能够实现让多个对象共享内存。

补充:在Java开发中,上下文类和工具类中一般会有大量的静态成员。

28.继承中变量的覆盖和输出

class Base {
    String a = "父类a";
    String  b = "父类b";
    static void a() {
        System.out.println("父类的静态A");
    }

    void b() {
        System.out.println("父类的B"+b);
    }

    void c(){
        System.out.println("父类的成员变量a:"+a);
    }

    void d(){
        System.out.println("父成员变量b:"+b);
    }
}

class Inherit extends Base {
    String a = "zi类a";
    String  b = "zi类b";
    static void a() {
        System.out.println("子类的静态C");
    }

    void b() {
        System.out.println("子类b:"+b);
    }
    void c(){
        System.out.println("子类的成员变量a:"+a);
    }

    public static void main(String args[]) {
        Base b = new Base();
        Inherit inherit = new Inherit();
        Base c = new Inherit();
        System.out.println("父类===================");
        b.a();//父类的静态A
        b.b();//父类的B
        System.out.println("子类===================");
        inherit.a();//子类掩盖了父类的静态方法,子类的静态C
        inherit.b();//子类b:zi类b,打印本身的
        inherit.c();//子类的成员变量a:zi类a,打印本身的
        inherit.d();//父成员变量b:父类b,调用父类的d方法,而且d方法里的成员变量a也是b的
        System.out.println("父类指向子类============");
        c.a();//父类的静态A
        c.b();//子类b:zi类b
        c.c();//子类的成员变量a:zi类a
        c.d();//父成员变量b:父类b
    }
}

子类覆盖了父类的方法,而且覆盖了父类的成员变量,而且在覆盖的方法中调用了这个覆盖的成员变量。这时候,调用这个覆盖的方法会调用覆盖的成员变量。若是子类只覆盖了成员变量,没有覆盖方法,调用这个方法会调用父类的成员变量,尽管这个成员变量被覆盖了。

2八、是否能够从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
答:不能够,静态方法只能访问静态成员,由于非静态方法的调用要先建立对象,在调用静态方法时可能对象并无被初始化。

2九、如何实现对象克隆? 
答:有两种方式: 
  1). 实现Cloneable接口并重写Object类中的clone()方法; 
  2). 实现Serializable接口,经过对象的序列化和反序列化实现克隆,能够实现真正的深度克隆,代码以下。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class MyUtil {

    private MyUtil() {
        throw new AssertionError();
    }

    public static <T> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();

        // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
        // 这两个基于内存的流只要垃圾回收器清理对象就可以释放资源,这一点不一样于对外部资源(如文件流)的释放
    }
}
View Code

注意:基于序列化和反序列化实现的克隆不只仅是深度克隆,更重要的是经过泛型限定,能够检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来老是优于把问题留到运行时。

30、GC是什么?为何要有GC? 
答:GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java提供的GC功能能够自动监测对象是否超过做用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操做方法。Java程序员不用担忧内存管理,由于垃圾收集器会自动进行管理。要请求垃圾收集,能够调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM能够屏蔽掉显示的垃圾回收调用。 
垃圾回收能够有效的防止内存泄露,有效的使用可使用的内存。垃圾回收器一般是做为一个单独的低优先级的线程运行,不可预知的状况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或全部对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,由于服务器端的编程须要有效的防止内存泄露问题,然而时过境迁,现在Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户一般以为iOS的系统比Android系统有更好的用户体验,其中一个深层次的缘由就在于Android系统中垃圾回收的不可预知性。

补充:垃圾回收机制有不少种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要建立的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,可是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不一样的区域,在垃圾收集过程当中,可能会将对象移动到不一样区域: 
- 伊甸园(Eden):这是对象最初诞生的区域,而且对大多数对象来讲,这里是它们惟一存在过的区域。 
- 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。 
- 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次彻底收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。

 

与垃圾回收相关的JVM参数:

  • -Xms / -Xmx — 堆的初始大小 / 堆的最大大小
  • -Xmn — 堆中年轻代的大小
  • -XX:-DisableExplicitGC — 让System.gc()不产生任何做用
  • -XX:+PrintGCDetails — 打印GC的细节
  • -XX:+PrintGCDateStamps — 打印GC操做的时间戳
  • -XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
  • -XX:NewRatio — 能够设置老生代和新生代的比例
  • -XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布
  • -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值
  • -XX:TargetSurvivorRatio:设置幸存区的目标使用率

3一、String s = new String("xyz");建立了几个字符串对象? 
答:两个对象,一个是静态区的"xyz",一个是用new建立在堆上的对象。

3二、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)? 
答:接口能够继承接口,并且支持多重继承。抽象类能够实现(implements)接口,抽象类可继承具体类也能够继承抽象类。

3五、内部类能够引用它的包含类(外部类)的成员吗?有没有什么限制? 
答:一个内部类对象能够访问建立它的外部类对象的成员,包括私有成员。

3六、Java 中的final关键字有哪些用法? 
答:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值之后值不能被修改(常量)。

4六、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,何时被执行,在return前仍是后? 
答:会执行,在方法返回调用者前执行。

注意:在finally中改变返回值的作法是很差的,由于若是存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕以后再向调用者返回其值,而后若是在finally中修改了返回值,就会返回修改后的值。显然,在finally中返回或者修改返回值会对程序形成很大的困扰

 4九、列出一些你常见的运行时异常? 
答: 
- ArithmeticException(算术异常) 
- ClassCastException (类转换异常) 
- IllegalArgumentException (非法参数异常) 
- IndexOutOfBoundsException (下标越界异常) 
- NullPointerException (空指针异常) 
- SecurityException (安全异常)

50、阐述final、finally、finalize的区别。 
答: 
- final:修饰符(关键字)有三种用法:若是一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,所以它和abstract是反义词。将变量声明为final,能够保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在之后的引用中只能读取不可修改。被声明为final的方法也一样只能使用,不能在子类中被重写。 
- finally:一般放在try…catch…的后面构造老是执行代码块,这就意味着程序不管正常执行仍是发生异常,这里的代码只要JVM不关闭都能执行,能够将释放外部资源的代码写在finally块中。 
- finalize:Object类中定义的方法,Java中容许使用finalize()方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。这个方法是由垃圾收集器在销毁对象时调用的,经过重写finalize()方法能够整理系统资源或者执行其余清理工做。

5二、List、Set、Map是否继承自Collection接口? 
答:List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不容许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。

5三、阐述ArrayList、Vector、LinkedList的存储性能和特性。 
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,它们都容许直接按序号索引元素,可是插入元素要涉及数组元素移动等内存操做,因此索引数据快而插入数据慢,Vector中的方法因为添加了synchronized修饰,所以Vector是线程安全的容器,但性能上较ArrayList差,所以已是Java中的遗留容器。LinkedList使用双向链表实现存储(将内存中零散的内存单元经过附加的引用关联起来,造成一个能够按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据须要进行前向或后向遍历,可是插入数据时只须要记录本项的先后项便可,因此插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此以外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,可是因为ArrayList和LinkedListed都是非线程安全的,若是遇到多个线程操做同一个容器的场景,则能够经过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另外一个类的构造器中建立新的对象来加强实现)。

补充:遗留容器中的Properties类和Stack类在设计上有严重的问题,Properties是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个Hashtable并将其两个泛型参数设置为String类型,可是Java API中的Properties直接继承了Hashtable,这很明显是对继承的滥用。这里复用代码的方式应该是Has-A关系而不是Is-A关系,另外一方面容器都属于工具类,继承工具类自己就是一个错误的作法,使用工具类最好的方式是Has-A关系(关联)或Use-A关系(依赖)。同理,Stack类继承Vector也是不正确的。Sun公司的工程师们也会犯这种低级错误,让人唏嘘不已。

5四、Collection和Collections的区别? 
答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操做,这些方法包括对容器的搜索、排序、线程安全化等等。

5五、List、Map、Set三个接口存取元素时,各有什么特色? 
答:List以特定索引来存取元素,能够有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系能够是一对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实如今插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。

5六、TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素? 
答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,可是要求传入第二个参数,参数是Comparator接口的子类型(须要重写compare方法实现元素的比较),至关于一个临时定义的排序规则,其实就是经过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。 

5七、Thread类的sleep()方法和对象的wait()方法均可以让线程暂停执行,它们有什么区别? 
答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其余线程,可是对象的锁依然保持,所以休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法致使当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),若是线程从新得到对象的锁就能够进入就绪状态。

5八、线程的sleep()方法和yield()方法有什么区别? 
答: 
① sleep()方法给其余线程运行机会时不考虑线程的优先级,所以会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会; 
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态; 
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; 
④ sleep()方法比yield()方法(跟操做系统CPU调度相关)具备更好的可移植性。

60、请说出与线程同步以及线程调度相关的方法。 
答: 
- wait():使一个线程处于等待(阻塞)状态,而且释放所持有的对象的锁; 
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; 
- notify():唤醒一个处于等待状态的线程,固然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM肯定唤醒哪一个线程,并且与优先级无关; 
- notityAll():唤醒全部处于等待状态的线程,该方法并非将对象的锁给全部线程,而是让它们竞争,只有得到锁的线程才能进入就绪状态;

提示:关于Java多线程和并发编程的问题,建议你们看个人另外一篇文章《关于Java并发编程的总结和思考》

补充:Java 5经过Lock接口提供了显式的锁机制(explicit lock),加强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通讯的Condition对象;此外,Java 5还提供了信号量机制(semaphore),信号量能够用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问以前,线程必须获得信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。

6一、编写多线程程序有几种实现方式? 
答:Java 5之前实现多线程有两种实现方法:一种是继承Thread类;另外一种是实现Runnable接口。两种方式都要经过重写run()方法来定义线程的行为,推荐使用后者,由于Java中的继承是单继承,一个类有一个父类,若是继承了Thread类就没法再继承其余类了,显然使用Runnable接口更为灵活。

补充:Java 5之后建立线程还有第三种方式:实现Callable接口,该接口中的call方法能够在线程执行结束时产生一个返回值

6四、启动一个线程是调用run()仍是start()方法? 
答:启动一个线程是调用start()方法,使线程所表明的虚拟处理机处于可运行状态,这意味着它能够由JVM 调度并执行,这并不意味着线程就会当即运行。run()方法是线程启动后要进行回调(callback)的方法。

6五、什么是线程池(thread pool)? 
答:在面向对象编程中,建立和销毁对象是很费时间的,由于建立一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每个对象,以便可以在对象销毁后进行垃圾回收。因此提升服务程序效率的一个手段就是尽量减小建立和销毁对象的次数,特别是一些很耗资源的对象建立和销毁,这就是”池化资源”技术产生的缘由。线程池顾名思义就是事先建立若干个可执行的线程放入一个池(容器)中,须要的时候从池中获取线程不用自行建立,使用完毕不须要销毁线程而是放回池中,从而减小建立和销毁线程对象的开销。 
Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤为是对于线程池的原理不是很清楚的状况下,所以在工具类Executors面提供了一些静态工厂方法,生成一些经常使用的线程池,以下所示: 
- newSingleThreadExecutor:建立一个单线程的线程池。这个线程池只有一个线程在工做,也就是至关于单线程串行执行全部任务。若是这个惟一的线程由于异常结束,那么会有一个新的线程来替代它。此线程池保证全部任务的执行顺序按照任务的提交顺序执行。 
- newFixedThreadPool:建立固定大小的线程池。每次提交一个任务就建立一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,若是某个线程由于执行异常而结束,那么线程池会补充一个新线程。 
- newCachedThreadPool:建立一个可缓存的线程池。若是线程池的大小超过了处理任务所须要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增长时,此线程池又能够智能的添加新线程来处理任务。此线程池不会对线程池大小作限制,线程池大小彻底依赖于操做系统(或者说JVM)可以建立的最大线程大小。 
- newScheduledThreadPool:建立一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。 
- newSingleThreadExecutor:建立一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

第60题的例子中演示了经过Executors工具类建立线程池并使用线程池执行线程的代码。若是但愿在服务器上使用线程池,强烈建议使用newFixedThreadPool方法来建立线程池,这样能得到更好的性能。

6八、Java中如何实现序列化,有什么意义? 
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。能够对流化后的对象进行读写操做,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操做时可能引起的问题(若是不进行序列化可能会存在数据乱序的问题)。 
要实现序列化,须要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,而后使用一个输出流来构造一个对象输出流并经过writeObject(Object)方法就能够将实现对象写出(即保存其状态);若是须要反序列化则能够用一个输入流创建对象输入流,而后经过readObject方法从流中读取对象。序列化除了可以实现对象的持久化以外,还可以用于对象的深度克隆(能够参考第29题)。

6九、Java中有几种类型的流? 
答:字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其余的流,主要是为了提升性能和使用方便。关于Java的I/O须要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不一样于C#的是它只有一个维度一个方向。

面试题 - 编程实现文件拷贝。(这个题目在笔试的时候常常出现,下面的代码给出了两种实现方案)

public static void fileCopy(String source,String targer) throws IOException {
        try(InputStream in = new FileInputStream(source)) {
            try(OutputStream out = new FileOutputStream(targer)) {
                byte[] buffer = new byte[3096];
                int byteToRead;
                while((byteToRead = in.read(buffer))!=-1){
                    out.write(buffer,0,byteToRead);
                }
            }
        }
    }
    public static void fileCopyNIO(String source,String target) throws IOException {
        try(FileInputStream in = new FileInputStream(source)) {
            try(FileOutputStream out = new FileOutputStream(target)) {
                FileChannel inChannel  = in.getChannel();
                FileChannel outChannel = out.getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(4096);
                while (inChannel.read(buffer)!=-1){
                    buffer.flip();
                    outChannel.write(buffer);
                    buffer.clear();
                }
            }
        }
    }
View Code

注意:上面用到Java 7的TWR,使用TWR后能够不用在finally中释放外部资源 ,从而让代码更加优雅。

70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。 

 /**
     *统计给定文件中字符word的个数
     */
    public static int countWordInFile(String filename,String word) throws IOException {
        int count = 0;
        try(FileReader fr = new FileReader(filename)){
            try(BufferedReader br = new BufferedReader(fr)){
                String line = null;
                while ((line = br.readLine())!=null){
                    int index = -1;
                    while (line.length()>=word.length() && (index=line.indexOf(word))>=0){
                        count++;
                        line = line.substring(index+word.length());
                    }
                }
            }
        }
        return count;
    }
View Code

7一、如何用Java代码列出一个目录下全部的文件? 

  /**
     * 列出当前文件夹下的文件
     */
    public void fileList(String source){
        File file = new File(source);
        for (File temp : file.listFiles()) {
            if (temp.isFile()){
                System.out.println(temp.getName());
            }
        }
    }

若是须要对文件夹继续展开,代码以下所示:

/**
     * 列出文件夹下的全部文件,深刻
     */
    private static void _walkDirectory(File f,int level) throws IOException {
        if (f.isDirectory()){
            writeTofile(f, level);
            for (File temp : f.listFiles()) {
                _walkDirectory(temp,level+1);
            }
        }else {
            writeTofile(f, level);
        }
    }

    private static void writeTofile(File f, int level) throws IOException {
        try(BufferedWriter bw = new BufferedWriter(new FileWriter(new File("F:\\listFile.txt"),true))){
            for (int i = 0; i < level - 1; i++) {
                System.out.print("----");
                bw.write("----");
            }
            System.out.println("|"+f.getName());
            bw.write("|"+f.getName());
            bw.newLine();
            bw.flush();
        }
    }

    public static void showDirectory(File f) throws IOException {
        _walkDirectory(f,0);
    }
    @Test
    public void testShow(){
        try {
            showDirectory(new File("D:\\MyApp"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
View Code

在java7中可使用NIO.2的api来作:

/**
     * 列出文件夹下的全部文件,深刻
     */
    @Test
    public void listFiles() throws IOException {
        Path path = Paths.get("D:\\MyApp");
        Files.walkFileTree(path,new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs){
                System.out.println(file.getFileName().toString());
                return FileVisitResult.CONTINUE;
            }
        });
    }
View Code

 7二、用Java的套接字编程实现一个多线程的回显(echo)服务器。 
答:

/**
 * socket多线程回显
 * Created by mrf on 2016/3/17.
 */
public class EchoServer {
    private static final int ECHO_SERVER_PORT = 6789;

    public static void main(String[] args) {
        try(ServerSocket server = new ServerSocket(ECHO_SERVER_PORT)) {
            System.out.println("=================================");
            System.out.println("============服务启动 =============");
            while (true){
                Socket client = server.accept();
                new Thread(new ClientHandler(client)).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static class ClientHandler implements Runnable{
        private Socket client;
        public ClientHandler(Socket client){
            this.client = client;
        }

        @Override
        public void run() {
            try(BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
                PrintWriter pw = new PrintWriter(client.getOutputStream())
            ) {
                String msg = br.readLine();
                System.out.println("收到"+client.getInetAddress()+"发送的:"+msg);
                pw.println(msg);
                pw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
 * 测试
 */
class EchoClient{
    public static void main(String[] args) throws IOException {
        Socket client = new Socket("localhost",6789);
        Scanner sc = new Scanner(System.in);
            System.out.println("请输入内容:");
            String msg = sc.nextLine();
            sc.close();
            PrintWriter pw = new PrintWriter(client.getOutputStream());
            pw.println(msg);
            pw.flush();
            BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
            System.out.println(br.readLine());
    }
}
View Code

7三、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 

答:xml文档定义分为DTD和Schema两种形式,两者都是对xml语法的约束,其本质区别在于Schema自己也是一个xml文件,能够被xml解析器解析,并且能够为xml承载的数据定义类型,约束能力较之DTD更强大。对xml的解析主要有

dom(文档对象模型,Document Object Model)、SAX(Simple API for xml)和StAx(java6中引入的新的解析xml的方式,Streaming API for xml),其中dom处理大型文件时其性能降低的很是厉害,这个问题是由DOM树结构占用的内存较多形成的,并且dom解析方式必须在解析文件以前把整个文件装入内存,适合对xml的随机访问(典型的空间换时间);sax是事件驱动的xml解析方法,它顺序读取xml文件,不须要一次所有装载整个文件。档遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户经过事件回调代码来处理xml文件,适合对xml的顺序访问;顾名思义,StAx把重点放在流上,实际上StAX与其余解析方式的本质区别就在于应用程序可以把xml作为一个事件流来处理。将xml作为一组事件来处理的想法并不新颖(sax就是这样作的),但不一样之处在于StAx容许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。

相关文章
相关标签/搜索