欢聚时代2017校招笔试题目(JAVA基础类)C卷

               欢聚时代2017校招笔试题目(JAVA基础类)C卷

 

以下运行结果哪个是正确的: ( )
 

class A{
public String show(D d){return ("AD");}
public String show(A a){return ("AA");}
}
 
class B extends A{
public String show(B b){return ("BB");}
public String show(A a){return ("BA");}
}
 
class C extends B{}
 
class D extends B{}
 
public class Test{
 
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        C c = new C();
        D d = new D();
        System.out.println(a.show(b)+"-"+a.show(c)+"-"+a.show(d)+"-"+b.show(a)+"-"+b.show(c)+"-"+b.show(d));
    }
}
AD-AA-AD-BA-BB-AD
AA-AD-AD-BA-BB-AD
AA-AA-AD-BA-BB-AD
AA-AA-AD-BA-BA-AD
答案:C
解析:

对象a有两个重载的show方法,分别是Show(D d),Show(A a);

对象b有三个重载的show方法,分别是Show(B b),Show(A a)以及show(D d)(相当于,因为B继承了A,所以有了Show(D d)和Show(A a)),但是B又重写了show(A a)方法,并新增了方法重载方法Show(B b);因此当调用b.show(a)时是调用的自己重写过的方法show(A a),当调用b.show(d)时直接调用从父类A继承来的方法。

a.show(b) 相当于对象a调用自己的方法show(A a),因为b是A类型,而不是D类型的;

a.show(c) 相当于对象a调用自己的方法show(A a),因为c是B类型,即A类型,而不是D类型;

a.show(d) 相当于对象a调用自己的方法show(D d),因为d是D类型的;

b.show(a) 相当于对象b调用自己的方法show(A a),因为a是A类型的;

b.show(c) 相当于对象b调用自己的方法show(B b),因为c是B类型的;

b.show(d) 相当于对象b调用父类A的方法show(D d)

总结一下就是,子类先找自己的方法,如果有就直接调用,若没有,则检查继承自父类的方法。

 

以下对继承的描述错误的是

Java中的继承存在着传递性
父类更具有通用性,子类更具体
定义为finally的类可以被继承
当实例化子类时会递归调用父类中的构造方法
答案:C

在同一包下,以下说法正确的是

super.方法()可以调用父类的所有非私有方法
super()可以调用父类的所有非私有构造函数
super.属性可以调用父类的所有非私有属性
this和super关键字可以出现在同一个构造函数中
答案:A B C D 
解析:super()和this()不能出现在同一个构造函数中,但这题说的是关键字

以下对重载描述错误的是

方法重载只能发生在一个类的内部
构造方法能重载,但静态方法不能重载
重载要求方法名相同,参数列表不同
方法的返回值类型不是区分方法重载的条件

答案:B

以下对接口描述错误的有

接口没有提供构造方法
接口中的方法默认使用public、abstract修饰
接口中的属性默认使用public、static、final修饰
接口不允许多继承

答案:D

以下关于内存管理描述错误的是

基本数据类型的变量、对象的引用及函数调用的现场保存都使用内存栈空间
通过new关键字和构造器创建的对象放在堆空间,类信息、常量、静态变量放在方法区
计数器是唯一一个没有规定任何OutOfMemoryError情况的区域
直接内存的分配不会受到Java堆大小的限制,所以不会抛OutOfMemoryError异常

答案:D
解析:

  • OutOfMemoryError异常 
    • 程序计数器:无
    • Java虚拟机栈: 如果虚拟机栈可扩展,扩展时无法申请到足够内存
    • 本地方法栈:与Java虚拟机栈相同
    • Java堆:堆中没有内存完成实例分配,并且堆无法再进行扩展
    • 方法区(运行时常量池):方法区无法满足内存分配需求(常量池无法申请到内存)
    • 直接内存:内存区域总和大于物理内存总和
  • StackOverflowError异常 
    • 程序计数器:无
    • Java虚拟机栈:线程请求的栈深度大于虚拟机所允许的深度
    • 本地方法栈:与Java虚拟机栈相同
    • Java堆:无
    • 方法区:无
    • 直接内存:无

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。

      在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

      显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括RAM及SWAP区或者分页文件)的大小及处理器寻址空间的限制。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

 

关于以下方法调用描述正确的是:
 

private static final List list = new ArrayList<>();
public static String test(String j){
    int i = 1, s = 1, f = 1, a = 1, b = 1,c = 1,d = 1,e = 1;
    list.add(new String("11111111111111111111111111111"));
    return test(s+i+f+a+b+c+d+e+"");
}
一定会发生” OutOfMemoryError: Java heap space”
一定会发生” StackOverflowError”
一定会发生” OutOfMemoryError: Java heap space与StackOverflowError”
当发生内存溢出错误时不需要用try…catch来捕获,需检查代码及jvm参数配置的合理性
答案:BD
解析:
递归时没有退出条件会发生栈溢出。
java.lang.Error: An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. 即:Error是Throwable的子类,用于标记严重错误。合理的应用程序不应该去try/catch这种错误。绝大多数的错误都是非正常的,就根本不该出现的。

    java.lang.Exception: The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch. 即Exception 是Throwable的一种形式的子类,用于指示一种合理的程序想去catch的条件。即它仅仅是一种程序运行条件,而非严重错误,并且鼓励用户程序去catch它。

下列关于堆内存管理描述错误的是
堆内存分为年轻代、老年代、持久代,持久代内存溢出为OutOfMemoryError: Java heap space
当Eden区满,再创建对象,会触发年轻代GC,不能回收的对象会放到To Survivor区
年轻代的To Survivor满了或里面的对象足够Old(超过XX:MaxTenuringThreshold),则这些对象被copy到老年代
当Old区被放满的之后,进行Full GC,Full GC后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,则出现OOM错误
答案:a
解析:内存申请过程

  1. JVM会试图为相关Java对象在Eden中初始化一块内存区域;
  2. 当Eden空间足够时,内存申请结束。否则到下一步;
  3. JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
  4. Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
  5. 当old区空间不够时,JVM会在old区进行major collection;

完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误"。

​以下关于异常的描述错误的是

RuntimeException:可以不使用try...catch进行处理,但是如果有异常产生,则异常将由JVM进行处理
finally语句块是不管有没有出现异常都要执行的内容
throw关键字可以在方法上声明该方法要抛出的异常
try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句
答案:C

以下代码输出结果是:
 

public class Demo {
    public static void main(String[] args) {
      try {
        System.out.println("try-");
        int[] a = new int[0];
        a[0] = 1;
        System.out.println(a[0]+”-”);
       return;
     } catch (Exception e) {
       System.out.println("catch-");
     } finally {
       System.out.println("finally");
     }
   }
}
try-1-catch-finally
try-1-finally
try-1-catch
try-catch-finally
答案:D

找出以下关于float的不正确的声明
float foo=-1;
float foo=1.0;
float foo=2.02f;
float foo=0x0123;
答案:b
解析:
float a = 1;这里的1是整形,当赋值给一个float时,存在一个隐式转换,不损失精度。 float a = 1f;1f就是float类型的。 如果你这样定义 f float f = 1.0; 肯定会出错。 因为1.0默认是double类型的。double范围比float大。会损失精度 float f = 1.0f; 这才是正确的
 

以下哪个是Java线程Thread启动的方法

run()
execute()
start()
go()
答案:c
 

可以让多个线程顺序执行的方法是

sleep()
wait()
interrupt()
join()
答案:d
 

下面那些类不是线程安全的

HashTable
ConcurrentHashMap
StringBuffer
HashMap
答案:D

 

在一个数据集合中,经常需要做数据查找的操作(不考虑线程安全),请问使用哪个数据结构最合适:

HashTable
LinkedList
HashMap
ConcurrentHashMap
解析:c
 

下面循环Map,错误的是
for (Map.Entry entry : map.entrySet()){}
while(map.hasNext()){}
for (Object key : map.keySet()){}
for (Object value : map.values()){}
答案:B
解析:hasNext()是 Iterator 的

Set keySet()-->Returns a Set view of the keys contained in this map. 

Set> entrySet()-->Returns a Set view of the mappings contained in this map. 

Collection values()-->Returns a Collection view of the values contained in this map. 

MAP中没有这个方法-->hasNext()

下面说法正确的是

LinkedList是使用了链表结果,特点是查找效率高
在高并发,多线程的情况下要使用高效率HashMap
ArrayList是一个动态数组能自动扩展,所以设置初始长度没必要
Set集合内元素不能重复,使用equals()来判断元素是否相等

答案:D

关于ArrayList,以下说法错误的是

ArrayList是容量可变的集合
ArrayList是线程安全的集合
ArrayList的元素是有序的
ArrayList可以存储重复的元素
答案:B

有关于反射说法错误的是

使用Class.forName("com.yy.xxx")方法获取类
class.newInstance()实例化一个对象
class.getDeclaredMethods()获取方法列表
method.invoke(obj, args)不能执行私有方法

答案:D

下面说法错误的是
通过代理Proxy.xxx()来执行被代理类的方法
java动态代理一般是用来做切面编程的,在被代理类的方法执行前后加入一些代码
动态代理中是通过反射的来执行被代理类的方法
java动态代理的代理类需要继承InvocationHandler接口

答案:D

现分别有一个通过socket发送文件的客户端以及一个通过socket接收文件的服务端:
a) 客户端从/tmp/src.data文件中读取文件内容,通过网络socket将文件内容发给服务端
b) 服务端监听10000端口,当10000端口接收到客户端连接请求时,从连接读取文件内容,并写入/tmp/dst.data中

1. 试写出客户端和服务端的具体实现代码(15分)
2. 如果客户端需要知道服务端已经完全接收到所有文件数据并成功写入/tmp/dst.data,有何实现方式?请说出你的思路(5分)

 

客户端参考实现如下:
public class MyClient {
 
public static void main(String[] args) {
MyClientmyClient = new MyClient();
myClient.sendFile();
    }
 
public void sendFile() {
        Socket socket = new Socket();
SocketAddresssocketAddress = new InetSocketAddress("127.0.0.1", 10000);
FileInputStreamfileInputStream = null;
OutputStreamoutputStream = null;
        // 定义缓冲区
byte[] buffer = new byte[1024];
try {
socket.connect(socketAddress);
fileInputStream = new FileInputStream("/tmp/src.data");
outputStream = socket.getOutputStream();
int length;
while ((length = fileInputStream.read(buffer, 0, buffer.length)) > 0) {
outputStream.write(buffer, 0, length);
outputStream.flush();
            }
        } catch (Exception e) {
e.printStackTrace();
        } finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
                } catch (Exception e) {
e.printStackTrace();
                }
            }
if (outputStream != null) {
try {
outputStream.close();
                } catch (IOException e) {
e.printStackTrace();
                }
            }
if (socket != null &&socket.isConnected()) {
try {
socket.close();
                } catch (IOException e) {
e.printStackTrace();
                }
            }
        }
    }
}
服务端参考实现如下:
public class MyServer {
 
public static void main(String[] args) {
MyServermyServer = new MyServer();
myServer.receiveFile();
    }
 
public void receiveFile() {
        Socket clientSocket = null;
InputStreaminputStream = null;
FileOutputStreamfileOutputStream = null;
        // 定义缓冲区
byte[] buffer = new byte[1024];
try {
ServerSocketserverSocket = new ServerSocket(10000);
clientSocket = serverSocket.accept();
inputStream = clientSocket.getInputStream();
fileOutputStream = new FileOutputStream("/tmp/dst.data");
int length;
while ((length = inputStream.read(buffer, 0, buffer.length)) > 0) {
fileOutputStream.write(buffer, 0, length);
fileOutputStream.flush();
            }
        } catch (Exception e) {
e.printStackTrace();
        } finally {
if (inputStream != null) {
try {
inputStream.close();
                } catch (Exception e) {
e.printStackTrace();
                }
            }
if (fileOutputStream != null) {
try {
fileOutputStream.close();
                } catch (IOException e) {
e.printStackTrace();
                }
            }
if (clientSocket != null &&clientSocket.isConnected()) {
try {
clientSocket.close();
                } catch (IOException e) {
e.printStackTrace();
                }
            }
        }
    }
}
 
第1个问题得分点如下:
1)       客户端正确使用socket以及socket.connect方法,得2分
2)       服务端正确使用serverSocket以及serverSocket.accept方法,得2分
3)       正确使用各类型的inputstream和outputstream(包括从socket中获取输入输出流,通过文件输入输出流访问文件内容),得5分
4)       使用缓冲区方式缓冲数据(byte数组或其他形式均可),得3分
5)       能通过判断数据读取长度或其他方式,使得程序可以正确判断出文件读写末尾,得3分。(上述答案参考实现中,实际上是通过客户端在写完文件内容后关闭socket输出流来使服务端可以得知输出流数据已经传输完毕。如果是长连接,则要通过其他方式,如在写文件前先写入文件size大小,知会服务端文件在socket已经写完)
6)       使用nio中的socketChannel以及bytebuffer方式进行IO读写,并且实现正确,前3个得分点也可得分
 
第2个问题,参考思路如下,其他思路只要合理,均可得分或酌情给分:
当服务端处理完毕后,向与客户端连接的socket的outputstream写回一个任意格式的ack信号数据,客户端从socket的inputstream收到ack信号后即可确认服务端已经处理完毕。

 

在业务开发中,为业务接口类中的方法处理前后添加相同的前置处理动作以及后置处理动作是很常见的场景。典型场景如:处理方法前添加日志记录,进行鉴权,记录方法处理时间等。试写出解决上述场景的具体代码,具体要求如下(20分):
1) 可以对任意业务接口的方法进行增强,添加执行方法的前后置逻辑
2) 不修改原有业务接口以及其具体实现类
3) 前后置逻辑可以按需动态地进行选择,如:可以只添加日志记录和进行鉴权,也可以只记录方法处理时间等
4) 根据你的设计,写出上述三个典型场景中至少一个的具体实现

参考的业务接口类定义及其实现(其他接口类定义不一一列出):
 

public interface MyBusiness {

 void xxx1(String msg);

 void xxx2(int value);

 }

 public class MyBusinessImpl implements MyBusiness {

 public void xxx1(String msg) {

 System.out.println(msg);

 }

 public void xxx2(int value) {

 System.out.println(value);

 }

 }
 

由于接口类不固定,核心思路是通过动态代理的方式对接口类方法进行增强,并通过责任链或类似的设计模式使得增强的逻辑组件化并可以动态扩展。参考实现代码如下:
执行类(调用责任链,传递执行):
public class HandlerInvocation {
private Iterator iterator;
private Object proxy;
private Method method;
private Object[] args;
publicHandlerInvocation(Object proxy, Method method, Object[] args, Iterator iterator) {
this.proxy = proxy;
this.method = method;
this.args = args;
this.iterator = iterator;
}
public Object invoke() throws Exception {
Object result = null;
if (iterator != null &&iterator.hasNext()) {
Interceptor interceptor = iterator.next();
result = interceptor.intercept(this);
} else {
result = method.invoke(proxy, args);
}
return result;
}
}
拦截类:
public interface Interceptor {
public Object intercept(HandlerInvocation invocation) throws Exception;
}
动态代理类实现(带main函数启动例子):
public class CommonProxy implements InvocationHandler {
private static ListinterceptorList = new ArrayList();
private Object target;
public static void initInterceptors(List list) {
interceptorList.addAll(list);
}
publicCommonProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HandlerInvocationhandlerInvocation = new HandlerInvocation(target, method, args, interceptorList.iterator());
returnhandlerInvocation.invoke();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add(new LoggerInterceptor());
CommonProxy.initInterceptors(list);
MyBusinessImplmyBusinessImpl = new MyBusinessImpl();
MyBusinessmyBusiness = (MyBusiness) Proxy.newProxyInstance(MyBusiness.class.getClassLoader(),
new Class[]{MyBusiness.class}, new CommonProxy(myBusinessImpl));
myBusiness.xxx1("aaa");
myBusiness.xxx2(123);
}
}
LoggerInterceptor实现参考:
public class LoggerInterceptor implements Interceptor {
public Object intercept(HandlerInvocation invocation) throws Exception {
System.out.println("pre handle");
Object result = invocation.invoke();
System.out.println("post handle");
return result;
}
}
得分点:
1) 利用Java动态代理或Cglib等高级实现方式实现对接口的代理,得8分
2) 利用责任链或通过对其他设计模式对增强处理进行接口抽象化,得8分
3) 根据相关设计写出任意一个典型增强场景的具体实现(日志记录,进行鉴权,记录方法处理时间),得4分


随机生成20个不重复的小写字母并进行排序,排序方式为倒序。

private static char getRamdomChar(Random rdm){
        int i = Math.abs(rdm.nextInt())%26+97;//产生97到122的随机数a-z值;
        i = i%26;
        i=i+97;
        return (char)i;
    }
 
    public static void main(String[] args) {
 
        TreeSettset = new TreeSet(new Comparator() {
 
            @Override
            public int compare(Character o1, Character o2) {
                if(o1>o2)
                   return -1;
                else if(o1

 

你可能感兴趣的:(面试珠玑)