加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【IT面试题——语言基础类】Java(一)

(2012-06-10 12:19:19)
标签:

it

分类: 技术类原创
1.日期操作

题目内容要求计算日期差,获得某天固定的月份,日期等。
用到Calendar类,详细可以参考这里

2.什么是泛型

泛型定义主要有以下两种(From wikipedia):
  • 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  • 在程序编码中一些包含参数的。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
一些强类型编程语言支持泛型,主要目的是加强类型安全减少类型转换次数

Java的泛型(Generics)是在JDK1.5推出的,主要目的是建立具有类型安全的集合框架,如:链表,散列映射等数据结构。
Java中泛型类声明格式为 class name<E>{};其中的E就是泛型,可以是任何对象或接口,但不能是基本数据类型
注意,Java的泛型基本上都是在Java编译器中而不是在运行库中实现的,所以在生成字节码的时候,产不多所有关于泛型类型的类型信息都被“擦掉”了。换句话说,编译器生成的代码和你用手工编写的不用泛型、检查了程序的安全类型之后进行强制类型转换多得到的代码基本相同[1]。

理解JAVA泛型的细节,参考以下文章:

3.创建一个Stack类(LIFO)

实际上Java已经有集合类LinkedList实现这样的功能,LinkedList的add方法和pop方法就对应了stack的push和pop方法。

Java的集合框架(Collection Framework)提供了一组精心设计的接口和类,对于常用的数据结构如映射(map),集(set),列表(list),树(tree),数组(array),哈希表(hash table),堆栈(stack)等提供了高效方便的实现。利用这些框架可以有效减少编程工作量,把精力放在更重要的部分。
集合框架中的接口以及类的关系如下图:
collection框架的应用非常广泛,在后面会慢慢补上各种练习题。

4.创建一个HashMap对象,添加一些学生的姓名和成绩:张三:90分,李四,83分。接着从HashMap中获取并他们的姓名和成绩,然后把李四的成绩改为100分,再次输出他们的信息。

这题用到集合框架中的HashMap,操作涉及到常用的put方法,还有Map的遍历(这里举出了Map的两种遍历方法)。代码如下:

     HashMap<String,Integer> map = new HashMap<String,Integer>();
        map.put("张三", 90);
        map.put("李四", 83);
        
        //遍历map的两种方法
        //1 通过keyset遍历
        Set<String> key = map.keySet();
        for (String str : key{
            System.out.println(str+"的成绩是"+map.get(str)+"分");
        }
        
        map.put("李四", 100);
        System.out.println("修改后:");
        

        //2 通过entryset遍历
        Set<Map.Entry<String, Integer>> entrySet  = map.entrySet();
        for (Map.Entry<String, Integer> entry : entrySet{
            System.out.println(entry.getKey()+"的成绩是"+entry.getValue()+"分"); 
        }
     
5.Write a singleton class.

现写一个单例模式的类。
Singleton,“单身”,在面向对象语言里通常翻译作单例(单一实例)。Singleton模式可以保证一个类只有一个实例,并只提供一个访问该实例的方法。
定义:单例模式就是确保一个类中只有一个实例,该实例必须自动创建,并向整个系统提供该实例。

总的来说,单例模式可以分为两种:饿汉式懒汉式(|||。。。)。其中饿汉是在系统启动一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。

一个简单的懒汉式单例类(Lazy Initialization):
  1. public class Singleton   
  2.     private static Singleton instance null  
  3.     private Singleton()   
  4.         // ....   
  5.       
  6.     public static Singleton getInstance()   
  7.         if (instance == null 
  8.             instance new Singleton();   
  9.          
  10.         return instance;   
  11.     }    
  12. }  

可以看到,单例模式是利用static关键字保证单实例的(实际上这种简单的实现只适合单线程的程序,后面会有多线程情况的实现)。补充一下java基础知识:

Java中按照是否是静态对类成员变量进行分类可以分为两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫做实例变量
两者的区别是:
对于静态变量在内存中只有一个拷贝,JVM只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配,可以用类名直接访问(方便),当然也可以通过对象访问(不推荐)。
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以有多个拷贝,互不影响(灵活)。

如前述,饿汉模式在一开始就进行了对象的实例化,示例代码如下:
  1. public class HungerSingleton {  
  2.     private static HungerSingleton instance new HungerSingleton(); 
  3.     private HungerSingleton()  
  4.     
  5.     public static HungerSingleton getInstance()  
  6.         return instance;  
  7.     }  
  8. }  

单例模式中一个重要的问题就是方法的同步问题,在多线程环境下,上面的问题还是可以产生多个实例,进一步可以加上同步机制(synchronized),示例代码如下:
  1. public class Singleton  
  2.       private static Singleton instance null 
  3.       private Singleton(){}  
  4.       synchronized static public Singleton getInstance()  
  5.           if (instance == null 
  6.               instance new Singleton();  
  7.            
  8.           return instance;  
  9.        
  10.   }  

一个需要注意的问题,在C中常用到的双重锁定检查(DCL),代码示例如下,实际上由于java编译器允许乱序执行,也存在失败的可能,因此不推荐使用。IBM developWorks上的这篇文章《双重检查锁定及单例模式》指出:无论以何种形式都不应该使用双重检查锁定,因为不能保证它在任何JVM实现上都能顺利运行。
  1. public class Singleton {  
  2.    
  3.       
  4.     private static Singleton instance null 
  5.    
  6.     public static Singleton getInstance()  
  7.         if (instance == null 
  8.             synchronized (Singleton.class 
  9.                 if (instance == null 
  10.                     instance new Singleton();  
  11.                  
  12.              
  13.          
  14.         return instance;  
  15.      
  16. }  

单例模式的缺点是不可继承
查到Effective Java中提到一个替代方法,有时间再看看。

参考文章:


6. what's the difference between heap and stack?

首先,Java中JVM里的堆(heap)和栈(stack)与C/C++中的堆和栈不是完全相同的。

C++中,内存会分为5个区,堆,栈,自由存储区,全局/静态存贮区,常量存贮区。

栈——由编译器自动分配释放,存放函数的参数值,局部变量的值等,操作方式为LIFO。
堆——一由有程序员分配释放,用new或malloc分配的自定义内容,用free/delete释放。
系统是用链表存储空闲内存地址的,所以这里的内存不是连续的,堆的大小受限于计算机系统中有效的虚拟内存。
    自由存储区(程序代码区)——存放函数二进制代码。
全局/静态存储区——存放全局变量和静态变量,程序结束后由系统释放。
文字常量区——存放常量字符串,程序结束后由系统释放。

在具体的C/C++编程框架中,这两个概念并不是并行的。对底层机器代码的研究可以揭示,栈是机器系统提供的数据结构,而堆则是C/C++函数库提供的。
    具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。 

和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free函数维护了一套内部的堆数据结构。

堆和栈的对比 

从以上知识可知,

  •      栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而堆是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。

  •     栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。



Java中,JVM运行java程序的过程中,包括了数个运行时数据区,参考 《Java内存管理:深入Java内存区域与OOM》,包括Java虚拟机栈(VM栈),本地方法栈,Java堆。

VM栈的生命周期与线程相同每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。
Java对于绝大多数应用来说,是虚拟机管理最大的一块内存。Java堆是被所有线程共享的,在虚拟机启动时创建。Java堆的唯一目的就是存放对象实例,绝大部分的对象实例都在这里分配。

简单来说,java中对象的引用以及原始类型变量都放在栈中,而new出来的对象(实例化的对象)都放在堆中。



7.What is shallow copy?

Java中的深拷贝与浅拷贝
Java里所有简单数据类型都是深拷贝。对象的拷贝则可以分为深拷贝和浅拷贝。

浅拷贝:拷贝对象时其实只是拷贝了指向原对象实例的引用,结果是多个引用指向一份拷贝。
深拷贝:所有变量都与原来对象值相同,但源对象中引用其他对象的变量将指向一份新的对象。
如图:
假设一个对象A的成员包括了对象A1和对象A2,
执行了 浅拷贝 B = A之后:
http://it.educity.cn/sun/images/20095208036.jpg而执行深拷贝之后(通过clone()方法或序列化,注意clone方法需要被引用对象也支持才行,当对象层次关系复杂时,clone方法会很麻烦):
对象实现Serializable接口后,可以用序列化的方法进行深拷贝,代码如下:

public static Object copy(Object oldObj){
        Object newObj = null;
        try {
            // write object
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(outputStream);
            out.writeObject(oldObj);
            out.flush();
            out.close();
            
            //read object
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream in = new ObjectInputStream(inputStream);
            newObj = in.readObject();
        
        } catch (Exception e{
            // TODO: handle exception
            e.printStackTrace();
        }
        return newObj;
        
    }

拷贝时:
Type a = new Type(); 
Type b = (Type)A.copy(a);
序列化方法的缺点是速度可能比较慢点。
clone方法的缺点是必须对每个对象的clone方法进行特别的实现,对象层次复杂到时候会很麻烦。

8. What is multiple inheritance?

多重继承
C++中允许一个子类继承多个父类,这种方式叫做多重继承。多重继承的功能很强,但使用起来复杂。
Java中不支持类的多重继承,但可以通过继承多个接口来实现类似的功能,同时避免了类的多重继承带来的麻烦。(extends xxx implements A,B,C,...)

9. What is polymorphism?

Java的多态性
一个子类可以定义它自己的特殊行为,同时又共享父类的一些特性,在不同情况下表现出不同的状态,这就叫多态。

一个典型的多态情景就是一个子类在调用某个方法时,根据参数的不同,可能调用是自己的方法或父类的方法,具体根据参数类型以及子类父类中方法的参数类型决定。
这句话是在太抽象了,直接上例子:
【IT面试题——语言基础类】Java(一)
http://s5/middle/a5527bf3gc25c1f4eaeb4&690
看清楚这几个类的继承关系以及方法重载关系没?看清楚了就继续看下面的程序:
     //先是变量初始化
     A a1 = new A();
     A a2 = new B();    
         B b = new B();;
         C c = new C();
         D d = new D();
         E e = new E();
     //下面看好了!
    System.out.println(a1.do(b));   //1
    System.out.println(a1.do(c));  //2
        System.out.println(a1.do(d));    //3
    System.out.println(a2.do(b));    //4
        System.out.println(a2.do(c));    //5
        System.out.println(a2.do(d));    //6
        System.out.println(b.do(b));     //7
        System.out.println(b.do(c));     //8
        System.out.println(b.do(d));     //9
    System.out.println(b.do(e));    //10

     看输出:
     A do A     (1)
     A do A (2)
     A do D (3)
     B do A (4)
     B do A (5)
     A do D (6)
     B do B (7)
     B do B (8)
     A do D (9)
     A do D (10)

看到(4)、(5)、(9)、(10)等结果是不是感觉很凌乱?
这其实是由于类多态的缘故,在调用过程中,涉及到方法优先级的问题,具体优先级如下:
1. this.method(O)
2. super.method(O)
3. this.method((Super)O);
4. super.method((Super)O);

上面的第10个例子就是走了从1到4这样一个查找图:
http://s2/middle/a5527bf3gc25c93e59e51&690
    最后查找到A.do(D),于是代码B.do(E)实际执行的是A.do(D)这个方法。

 另一个例子是第4个,查找图如下:
http://s11/middle/a5527bf3gc25ca77827ca&690
    最后查找到A.do((super)B),但由于这里A引用的是B类型的实例,而A类的do((super)B)方法(即do(A)方法)在其子类B中已经被重写(overriding),所以实际执行的是B.do(A)。于是,明明是A.do(B)却执行的是B.do(A)的代码。


10.Java中异常的分类及区别

分类如下图:
http://www.edujy.com/UploadFiles/WSXY/2009/10/200910271440323319.jpgError由JVM抛出,是程序不可控的错误,无法在程序里处理
Error对象及其子类不应该被抛出。

Exception又分为Runtime Exception(运行时异常CheckedException两种,运行时异常是在Java虚拟机正常运行期间抛出的异常的超类一般会默认处理,不用显式的throw或catch。
运行时异常一般是由于程序逻辑上没有满足语法要求(如数组越界,指针(这里是引用)为空)产生的。
除了运行时异常的其他异常都属于CheckedException,Java编译器要求程序必须捕获或声明抛出这种异常。





0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有