前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >工程师笔试题1(答案解析)

工程师笔试题1(答案解析)

作者头像
Java学习
发布2018-04-13 14:32:32
6850
发布2018-04-13 14:32:32
举报
文章被收录于专栏:java学习java学习
1.访问修饰符作用范围由大到小是( )。

A.private-protected-default-publicB.public-protected-default-private

C.private-default-protected-publicD.public-default-protected-private

2.在 Java 语言中,下面接口以键-值对的方式存储对象的是( )。

A.java.util.List B.java.util.Map

C.java.util.Collection D.java.util.Set

3.以下不是 Object 类的方法的是( )。

A.hashCode() B.finalize()

C.notify() D.hasNext()

4.有如下代码:

public class Test

{

public void change(String str, char ch[])

{

str = "test ok";

ch[0] = 'g';

}

public static void main(String args[])

{

String str = new String("good");

char[] ch = { 'a', 'b', 'c' };

Test ex = new Test();

ex.change(str, ch);

System.out.print(str + "and ");

System.out.print(ch);

}

}

上面程序的运行结果是()。

A.good and abc B.good and gbc

C.test ok and abc D.test ok andgbc

二、填空题

1.Math.round(12.5)的返回值等于( ),Math.round(-12.5)的返回值等于( )。

2.有如下程序:

String str1="hello world";

Stringstr2="hello"+newString("world");

System.out.println (str1==str2);

那么程序的运行结果是()。

3.在 Java 语言中,基本数据类型包括( )、字符类型( )、布尔类型boolean 和数

值类型()。

4.字符串分为两大类:一类是字符串常量( );另一类是字符串变量( )。

三、简答题

1.接口和抽象类有什么区别?

2.实现多线程的方法有哪几种?

3.利用递归方法求 6!

4.用 Java 语言实现一个观察者模式。

5.一个有 10 亿条记录的文本文件,已按照关键字排好序存储,请设计一个算法,可以从文件中快速查找指定关键字的记录。


答案解析


一、选择题

1.答案:B。

分析:本题考察的是 Java 语言中访问修饰符作用范围的知识。在 Java 语言中,类的权限访问修饰符有以下几种:private、default(package)、protected和 public。下面具体对这几个权限访问修饰符进行介绍。

(1)私有权限(private)

private 可以修饰数据成员、构造方法及方法成员,不可以修饰类(此处指外部类,不考虑内部类)。被 private 修饰的成员,只能在定义它们的类中使用,在其他类中不能调用。

(2)默认权限(default)

类、数据成员、构造方法和方法成员等都能够使用默认权限,即不被private、protected和 public 修饰。默认权限即同包权限,同包权限的元素只能在定义它们的类中以及同包的类中被调用。

(3)受保护权限(protected)

protected 可以修饰数据成员、构造方法和方法成员,不可以修饰类(此处指外部类,不考虑内部类)。被 protected 修饰的成员,能在定义它们的类中以及同包的类中被调用。如果有不同包的类想调用它们,那么这个类必须是它的子类。

(4)公共权限(public)

public 可以修饰类、数据成员、构造方法及方法成员。被 public 修饰的成员,可以在任何一个类中被调用,不管同包或不同包,是权限最大的一个修饰符。

以上几种修饰符的使用范围见表 2(表中√表示可访问,×表示不可访问)。

通过表 2 可知,访问修饰符的作用范围由大到小依次是 public、protected、default 和private。所以,选项 B 正确。

所以,本题的答案为 B。

2.答案:B。

分析:本题考察的是 Java 语言中基本数据结构的知识。

对于选项 A,List 中保存了相同类型的多个元素,元素是按照存入的顺序存储的,元素可以重复。所以,选项 A 错误。对于选项 B,Map 是以键-值对的方式来存储对象的,并且键不允许重复。所以,选项

B 正确。

对于选项 C,java.util.Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。而 Set 与 List 是它的两个具体的接口,由于 Set 与 List 都不是以键-值对的方式来存储对象的,因此,Collection 接口也不是。所以,选项 C 错误。对于选项 D,Set 中也保存了相同类型的多个元素,元素是不能重复的。所以,选项 D错误。

各接口的区别见表 3。

所以,本题的答案为 B。

3.答案:D。

分析:本题考察的是 Object 类的知识。Object 类是类层次结构的根,在 Java 语言中,所有的类从根本上而言都继承自这个类。而且,Object 类是 Java 语言中唯一没有父类的类,而其他所有的类,包括标准容器类,例如数组,都继承了 Object 类。

具体而言,Object 类的方法见表 4。

由此可见,Object 类没有 hasNext()方法。所以,选项 D 正确。

所以,本题的答案为 D。

4.答案:B。

分析:本题考察的是 Java 语言中传参方式以及不可变类的知识。不可变类(Immutable class)是指当创建了这个类的实例后,就不允许修改它的值了,也就是说,一个对象一旦被创建出来,在其整个生命周期中,它的成员变量就不能被修改了。它有点类似于常量(const),即只允许其他程序进行读操作,而不允许其他程序进行修改操作。在 Java 类库中,所有基本类型的包装类都是不可变类,例如 Integer、Float 等。此外,String 也是不可变类。可能有人会有疑问,既然String 是不可变类,为什么还可以写出如下

代码来修改 String 类型的值呢?

public class Test

{

public static void main(String[] args)

{

String s="Hello";

s+=" world";

System.out.println(s);

}

}

程序的运行结果为Hello world表面上看,s+="world"的作用好像是修改 String 类型对象 s 的值,其实不是,String s="Hello"语句声明了一个可以指向String 类型对象的引用,这个引用的名字为 s,它指向了一个字符串常量"Hello"。s+="world"并没有改变 s 所指向的字符串的值(由于"Hello"是 String类型的对象,而 String 又是不可变量),在这行代码运行后,s 指向了另外一个 String 类型的对象,该对象的内容为"Hello world"。原来的那个字符串常量"Hello"还存在于内存中,并没有被改变。在 Java 语言中,除了 8 种原始的数据类型(分别为byte、short、int、long、float、double、char 和 boolean)外,其他的类型都是对象,在方法调用的时候,传递的都是引用。引用从本质上来讲也是按值传递,只不过传递的这个值是对象的引用而已,因此,在方法调用的时候,对形参引用所指对象属性值的修改对实参可见。但是对引用值本身的修改对实参是不可见的。回到本题中来,在调用 change 方法的时候,change 方法的形参 str 实际上是实参 str (main方法中的 str)的一个副本,由于 String 是不可变量,因此,无法通过 str 来修改这个字符串的内容,执行语句 str="test ok"的结果是使形参的 str 指向了另外一个常量字符串(可以理解为修改了形参的值,而不是修改了形参所指向对象的值),但是这个修改对实参是不可见的,调用 change 方法结束后对象的 main 方法中 str 的值还是"good",而 change 方法的形参 ch 也是实参 ch(main 方法中的 ch 值)的一个副本,但是在这个方法中通过形参 ch 修改了实参ch 所指向对象的值,即数组元素的值,形参 ch与实参 ch 指向的是同一个对象,因此,通过形参对这个对象值的修改对实参是可见的,所以,当调用 ex.change 方法后,main 方法中 ch指向的数组的值变为{‘g’,‘b’,‘c’},因为该方法只是改变了 ch[0]的值而已,所以,程

序最终输出为字符串"good and gbc"。所以,选项 B 正确。

所以,本题的答案为 B。

二、填空题

1.答案:13,-12。

分析:本题考察的是 Math 类中 round 方法的使用。

Math 类主要提供了下面 5 个与取整相关的方法:

1)static doubleceil(double a):返回大于等于 a 的最小整数。

2)static doublefloor(double a):返回小于等于 a 的最大整数。

3)static doublerint(double a):四舍五入方法,返回与 a 的值最相近的整数,为 double类型。

4)static longround(double a):四舍五入方法,返回与 a 的值最相近的长整型数。

5)static intround(float a):四舍五入方法,返回与 a 的值最相近的整型数。

对于本题而言,round 是一个四舍五入的方法,12.5 的小数部分为 0.5,当对其执行Math.round()操作时,结果需要四舍五入,所以,结果为13;-12.5 的小数部分也为 0.5,当对其执行 Math.round()操作时,结果也需要四舍五入,由于-12>-13,因此,结果为-12。

2.答案:false。

分析:本题考察的是字符串知识。在 Java 语言中,除了 8 种基本的数据类型外,其他的类型都为引用类型,因此,语句str1==str2 的功能是比较 str1 与 str2 这两个字符串的地址是否相同,显然,str1 存储在常量区,而 str2 中的“world”是在堆空间上申请的另外一块存储空间,因此,二者必然有不同

的存储地址。因此,程序的运行结果为 false。

3.答案:浮点型 float、double,char,byte、short、int、long。分析:本题考察的是 Java 数据类型的知识。Java 语言中只有 8 种基本数据类型,分别为byte、short、int、long、float、double、char和 boolean。在方法调用传参时,这 8 种基本数据类型都是按值传递的,除此之外,所有的数据类型都是按引用传递的。由以上分析可知,本题的答案为:浮点型float、double,字符类型char,布尔类型boolean,数值类型 byte、short、int、long。

4.答案:String,StringBuffer。

分析:本题考察的是对 Java 字符串的理解。在 Java 语言中,String 是不可变类,也就是说,String 对象一旦被创建,其值将不能被改变,而 StringBuffer 是可变类,当对象被创建后,仍然可以对其值进行修改。由于 String是不可变类,因此,适合在需要被共享的场合中使用,而当一个字符串经常需要被修改时,最好使用 StringBuffer 来实现。如果使用 String 来保存一个经常被修改的字符串,在字符串被修改的时候会比 StringBuffer 多了很多附加的操作,同时会生成很多无用的对象,由于这些无用的对象会被垃圾回收器回收,所以,会影响程序的性能。在规模小的项目中这种影响很小,但是在一个规模大的项目中,这会给程序的运行效率带来很大的负面影响。

三、简答题

1.答案:接口(interface)和抽象类(abstractclass)是支持抽象类定义的两种机制(注意,该句中前后两个抽象类的意义不一样,前者表示的是一个实体,后者表示的是一个概念)。两者具有很大的相似性,甚至有时候是可以互换的。但同时,两者也存在很大的区别。具体而言,接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,如果一个类中包含抽象方法,那么这个类就是抽象类。在 Java 语言中,可以通过把类或者类中的某些方法声明为 abstract(abstract 只能用来修饰类或者方法,不能用来修饰属性)来表示一个类是抽象类。接口就是指一个方法的集合,接口中的所有方法都没有方法体,在 Java 语言中,接口是通过关键字 interface 来实现的。包含一个或多个抽象方法的类就必须被声明为抽象类,抽象类可以声明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体。在抽象类的子类中,实现方法必须含有相同的或者更低的访问级别(public->protected->private)。抽象类在使用的过程中不能被实例化,但是可以创建一个对象使其指向具体子类的一个实例。抽象类的子类为父类中所有的

抽象方法提供具体的实现,否则,它们也是抽象类。接口可以被看作是抽象类的变体,接口中所有的方法都是抽象的,可以通过接口来间接地实现多重继承。接口中的成员变量都是static final 类型,由于抽象类可以包含部分方法的实现,所以,在一些场合下抽象类比接口存在更多的优势。

接口与抽象类的相同点如下:

1)都不能被实例化。

2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能被实例化。接口与抽象类的不同点如下:

1)接口只有定义,不能有方法的实现,而抽象类可以有定义与实现,即其方法可以在抽象类中被实现。

2)实现接口的关键字为 implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类,因此,使用接口可以间接地达到多重继承的目的。

3)接口强调特定功能的实现,其设计理念是“has-a”关系,而抽象类强调所属关系,其设计理念为“is-a”关系。

4)接口中定义的成员变量默认为 publicstatic final,只能够有静态的不能被修改的数据成员,而且,必须给其赋初值,其所有的成员方法都是 public、abstract 的,而且只能被这两个关键字修饰。而抽象类可以有自己的数据成员变量,也可以有非抽象的成员方法,而且,抽象类中的成员变量默认为 default,当然也可以被定义为 private、protected 和 public,这些成员变量可以在子类中被重新定义,也可以被重新赋值,抽象类中的抽象方法(其前有

abstract 修饰)不能用 private、static、synchronized和 native 等访问修饰符修饰,同时方法必须以分号结尾,并且不带花括号{}。所以,当功能需要累积时,使用抽象类;不需要累积时,使用接口。

5)接口被运用于实现比较常用的功能,便于日后维护或者添加删除方法,而抽象类更倾向于充当公共类的角色,不适用于日后重新对里面的代码进行修改。

2.答案:Java 虚拟机(JavaVirtual Machine,JVM,是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境)允许应用程序并发地运行多个线程。在 Java 语言中,多线程的实现一般有以下三种方法:

1)实现 Runnable 接口,并实现该接口的run()方法。

以下是主要步骤:

①自定义类并实现 Runnable 接口,实现 run()方法。

②创建 Thread 对象,用实现 Runnable 接口的对象作为参数实例化该 Thread 对象。

③调用 Thread 的 start()方法。

class MyThread implements Runnable

{ //创建线程类

public void run()

{

System.out.println("Threadbody");

}

}

public class Test

{

public static void main(String[] args)

{

MyThread thread=new MyThread();

Thread t=new Thread(thread);

t.start(); //开启线程

}

}

2)继承 Thread 类,重写 run 方法。Thread 本质上也是实现了 Runnable 接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过 Thread 类的 start()方法。start()方法是一个 native(本地)方法,它将启动一个新线程,并执行 run()方法(Thread 中提供的 run()方法是一个空方法)。这

种方式通过自定义类直接 extends Thread,并重写 run()方法,就可以启动新线程并执行自己定义的 run()方法。需要注意的是,当 start()方法调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行多线程代码是由操作系统决定的。下例给出了 Thread 的使用方法。

class MyThread extends Thread

{ //创建线程类

public void run()

{

System.out.println("Threadbody"); //线程的方法体

}

}

public class Test

{

public static void main(String[] args)

{

MyThread thread=new MyThread();

thread.start(); //开启线程

}

}

3)实现 Callable 接口,重写 call()方法。

Callable对象实际是属于Executor框架中的功能类,Callable接口与Runnable接口类似,但是提供了比 Runnable 更强大的功能,主要表现为以下三点:

① Callable 可以在任务结束后提供一个返回值,Runnable 无法提供这个功能。

② Callable 中的 call()方法可以抛出异常,而 Runnable 的 run()方法不能抛出异常。

③运行 Callable 可以拿到一个 Future 对象,Future 对象表示异步计算的结果。它提供了检查计算是否完成的方法。由于线程属于异步计算模型,所以无法从其他线程中得到方法的返回值,在这种情况下,就可以使用 Future 来监视目标线程调用 call()方法的情况,当调用 Future 的 get()方法以获取结果时,当前线程就会阻塞,直到 call()方法结束返回结果。

示例代码如下所示:

import java.util.concurrent.*;

public class CallableAndFuture

{

// 创建线程类

public static class CallableTest implementsCallable<String>

{

public String call() throws Exception

{

return "Hello World!";

}

}

public static void main(String[] args)

{

ExecutorService threadPool =Executors.newSingleThreadExecutor();

// 启动线程

Future<String> future =threadPool.submit(new CallableTest());

try

{

System.out.println("waiting thread tofinish");

System.out.println(future.get()); // 等待线程结束,并获取返回结果

}

catch (Exception e)

{

e.printStackTrace();

}

}

}

上述程序的输出结果为

waiting thread to finish

Hello World!

在以上三种方式中,前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。当需要实现多线程时,一般推荐实现 Runnable 接口的方式,原因如下:首先,Thread 类定义了多种方法可以被派生类使用或重写,但是只有 run 方法是必须被重写的,在 run 方法中实现这个线程的主要功能。这当然是实现 Runnable 接口所需的同样的方法。而且,很多Java 开发人员认为,一个类仅在它们需要被加强或修改时才会被继承。因此,如果没有必要重写 Thread 类中的其他方法,那么通过继承 Thread 的实现方式与实现 Runnable 接口的效

果相同,在这种情况下最好通过实现 Runnable 接口的方式来创建线程。

3.答案:本题考察的是递归知识。

使用递归时,关键问题是要明白递归表达式的含义以及递归的终止条件。

实现代码如下:

代码语言:javascript
复制
public class Test
{
public static long fac(int n)
{
if(n > 1)
return (n * fac(n - 1));
else
return 1;
}
public static void main(String args[])
{
System.out.println(fac(6));
}
}

程序的运行结果为

720

4.答案:观察者模式(也被称为发布/订阅模式)提供了避免组件之间紧密耦合的另一种方法,它将观察者和被观察的对象分离开。在该模式中,一个对象通过添加一个方法(该方法允许另一个对象,即观察者注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者收到消息后所执行的操作与可观察的对象无关,这种模式使得对象可以相互对话,而不必了解原因。Java 语言与 C#语言的事件处理机制就是采用的此种设计模式。例如,用户界面(同一个数据可以有多种不同的显示方式)可以作为观察者,业务数据是被观察者,当数据有变化后会通知界面,界面收到通知后,会根据自己的显示方式修改界面的显示。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将它做好。观察者模式在模块之间划定了清晰的界

限,提高了应用程序的可维护性和重用性。设计类图如图 1 所示。

图 1 设计类图

下面给出一个观察者模式的示例代码,代码的主要功能是实现天气预报,同样的温度信息可以有多种不同的展示方式:

代码语言:javascript
复制
import java.util.ArrayList;
interface Subject
{
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
class Whether implements Subject
{
private ArrayList<Observer>observers=new ArrayList<Observer>();
private float temperature;
@Override
public void notifyObservers() {
for(int i=0;i<this.observers.size();i++)
{
this.observers.get(i).update(temperature);
}
}
@Override
public void registerObserver(Observer o) {
this.observers.add(o);
}
@Override
public void removeObserver(Observer o) {
this.observers.remove(o);
}
public void whetherChange() {
this.notifyObservers();
}
public float getTemperature(){
return temperature;
}
public void setTemperature(floattemperature) {
this.temperature = temperature;
notifyObservers();
}
}
interface Observer
{
//更新温度
public void update(float temp);
}
class WhetherDisplay1 implements Observer
{
private float temprature;
public WhetherDisplay1(Subject whether){
whether.registerObserver(this);
}
@Override
public void update(float temp) {
this.temprature=temp;
display();
}
public void display(){
System.out.println("display1****:"+this.temprature);
}
}
class WhetherDisplay2 implements Observer
{
private float temprature;
public WhetherDisplay2(Subject whether)
{
whether.registerObserver(this);
}
@Override
public void update(float temp) {
this.temprature=temp;
display();
}
public void display()
{
System.out.println("display2----:"+this.temprature);
}
}
public class Test
{
public static void main(String[] args)
{
Whether whether=new Whether();
WhetherDisplay1 d1=newWhetherDisplay1(whether);
WhetherDisplay2 d2=newWhetherDisplay2(whether);
whether.setTemperature(27);
whether.setTemperature(26);
}
}

5.答案:10 亿条记录对应的数据量在GB 量级,对于普通的计算机来讲,没有这么大的内存空间供使用,因此,无法一次把这些数据信息全部都读到内存中进行处理,需要对问题进行分解,例如把数据分成 100 份,每一份就是 10MB 量级,基本上放入内存无压力了。把这 10 亿记录,均分为 100 份,把每份的第一条记录关键字和此记录对应的文件偏移量先扫入内存(类似索引),这里需要磁盘随机 IO100 次。这样可以马上定位出指定关键字所在的记录块,把相应的记录块拿到内存,二分查找即可。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档