【IT面试题——语言基础类】Java(一)
标签:
it |
分类: 技术类原创 |
1.日期操作
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的两种遍历方法)。代码如下:
5.Write a singleton
class.
现写一个单例模式的类。
Singleton,“单身”,在面向对象语言里通常翻译作单例(单一实例)。Singleton模式可以保证一个类只有一个实例,并只提供一个访问该实例的方法。
定义:单例模式就是确保一个类中只有一个实例,该实例必须自动创建,并向整个系统提供该实例。
总的来说,单例模式可以分为两种:饿汉式和懒汉式(|||。。。)。其中饿汉是在系统启动一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。
一个简单的懒汉式单例类(Lazy
Initialization):
-
public
class Singleton { -
private static Singleton null;instance = -
private Singleton() { -
// .... -
} -
public static Singleton getInstance() { -
if (instance null)== { -
instance = new Singleton(); -
} -
return instance; -
} -
}
可以看到,单例模式是利用static关键字保证单实例的(实际上这种简单的实现只适合单线程的程序,后面会有多线程情况的实现)。补充一下java基础知识:
Java中按照是否是静态对类成员变量进行分类可以分为两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫做实例变量。
两者的区别是:
对于静态变量在内存中只有一个拷贝,JVM只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配,可以用类名直接访问(方便),当然也可以通过对象访问(不推荐)。
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以有多个拷贝,互不影响(灵活)。
如前述,饿汉模式在一开始就进行了对象的实例化,示例代码如下:
-
public
class HungerSingleton { -
private static HungerSingleton newinstance = HungerSingleton(); -
private HungerSingleton() { -
} -
public static HungerSingleton getInstance() { -
return instance; -
} -
}
单例模式中一个重要的问题就是方法的同步问题,在多线程环境下,上面的问题还是可以产生多个实例,进一步可以加上同步机制(synchronized),示例代码如下:
-
public
class Singleton { -
private static Singleton null;instance = -
private Singleton(){} -
synchronized static public Singleton getInstance() { -
if (instance null)== { -
instance = new Singleton(); -
} -
return instance; -
} -
}
一个需要注意的问题,在C中常用到的双重锁定检查(DCL),代码示例如下,实际上由于java编译器允许乱序执行,也存在失败的可能,因此不推荐使用。IBM
developWorks上的这篇文章《双重检查锁定及单例模式》指出:无论以何种形式都不应该使用双重检查锁定,因为不能保证它在任何JVM实现上都能顺利运行。
-
public
class Singleton { -
-
-
private static Singleton instance null;= -
-
public static Singleton getInstance() { -
if (instance null)== { -
synchronized (Singleton. class){ -
if (instance null)== { -
instance = new Singleton(); -
} -
} -
} -
return instance; -
} -
}
单例模式的缺点是不可继承。
查到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;

加载中…