次のコードを確認してください。
class OutOfInkException extends Exception { }
class Pen {
void write()
throws OutOfInkException { }
protected void
refillInk() throws Exception { }
}
class BallpointPen extends Pen {
private void
write() throws OutOfInkException { } //
line 1
public void
refillInk() { } // line 2
}
public class Test {
public static
void main(String[] args) throws Exception {
Pen p = new
BallpointPen();
p.write();
p.refillInk();
}
}
このコードを実行すると、どのような結果になりますか。
1.
line 1が原因でコンパイルに失敗する。
2.
line 2が原因でコンパイルに失敗する。
3.
line 1と line 2が原因でコンパイルに失敗する。
4.
コンパイルは正常に成功するが、実行時例外が発生する。
5.
コンパイル、実行ともに問題なく成功する。
解答: 1
1
解説:
メソッドのオーバーライドに関するルールの問題です。
メソッドをオーバーライドする際は、以下のルールにもとづく必要があります。
A)メソッド名と引数リストが同じであること
B)戻り値がスーパークラスのメソッドの型と同じか、その型を継承した型であること(共変であるといいます)
C)throws句で指定できる例外はスーパークラスのメソッドの例外と同じか、その例外を継承した例外であること、もしくはthrows句を指定しないこと
D)アクセス修飾子がスーパークラスのメソッドと同じか、それよりもゆるい(※)ものであること
line 1は、Dのルールを満たしていないのでコンパイルエラーになります。
line 2は、すべてのルールを満たしているので、コンパイルエラーにはなりません。(throwsが省略されているのは、ルールCにより問題なし)
※アクセス修飾子はゆるい方から順に、public > protected > アクセス修飾子なし(パッケージ・プライベート) > private となります
次のコードを確認してください。
class Car {
int cno;
String name;
public Car(int
cno, String name) {
this.cno =
cno;
this.name =
name;
}
public boolean
equals(Object obj) {
if (obj ==
null) { return false; }
Car other =
(Car) obj;
if
(this.name.equals(other.name)) {
return
true;
} else {
return
false;
}
}
}
public class Test {
public static
void main(String[] args) {
Car c1 =
new Car(1234, "Taxi");
Car c2 =
c1;
Car c3 =
new Car(9876, "Taxi");
System.out.println(c1.equals(c2));
System.out.println(c1 == c2);
System.out.println(c1.equals(c3));
System.out.println(c1 == c3);
}
}
このコードを実行すると、trueはいくつ表示されますか。
1.
1つ
2.
2つ
3.
3つ
4.
4つ
解答: 3
解説:
等価演算子(==)とequalsメソッドに関する問題です。
等価演算子は、比較対象の2つのインスタンスで参照している実体が同一かどうかについて、同一であればtrueを返します。
Carクラス型の参照変数c2は、c1を代入(つまり参照のコピーを)しているので、(c1 == c2)はtrueになります。
逆に、c3はc1とは別に新たにインスタンス化をしているので、実体としては別物となり、(c1 == c3)はfalseになります。
また、equalsメソッドはすべてのクラスの暗黙のスーパー・クラスであるObjectクラスで定義されており、そこでは等価演算子と同じ結果を返します。
この問題では、Carクラスでequalsメソッドがオーバー・ライドされており、インスタンス変数のうちnameが等しければtrueを返すような実装となっています。
そのため、c1, c2, c3どれもnameが"Taxi"であるオブジェクトなので、(c1.equals(c2))も(c1.equals(c3))もtrueとなります。
結果として、出力は
true
true
true
false
となり、trueは3つ表示されます。
また、equalsメソッドをオーバー・ライドした場合はhashCodeも併せて適切にオーバー・ライドしないと、コレクション・フレームワーク(List, Set, Map etc.)などを使用した際に不適切な挙動となるので注意が必要です。
次のコードを確認してください。
interface Movable {
void walk();
}
abstract class Machine { }
class Robot extends Machine implements Movable {
void walk() { }
}
final class Accounts {
public static
void expire();
}
class Ship {
public void
depart() {
class
Anchor { }
}
}
正常にコンパイルができるクラスは、どれですか。
1.
Machineのみ
2.
Robotのみ
3.
MachineとRobot
4.
MachineとShip
5.
RobotとAcounts
6.
RobotとShip
解答: 4
解説:
クラスの種類と定義のルールに関する問題です。
抽象メソッドを含むクラスは抽象クラスにする必要がありますが、抽象クラスであるからといって必ずしも抽象メソッドを含んでいなければならないわけではないので、Machineクラスは問題なくコンパイルに成功します。
次のRobotクラスについては、インタフェースMovableを実装しているので、抽象メソッドのwalkをオーバー・ライドする必要があります。
ただし、注意するべきはインタフェースに定義されたメソッドのデフォルトの修飾子がpublic abstractになるという点で、そのためRobotクラスではアクセス修飾子にpublicしか指定できません。
ここではwalkにアクセス修飾子が指定されておらず、パッケージ・プライベートとなって狭めてしまっているためにコンパイルエラーになります。(問題1の解説を参照)
また、Accountsクラスにあるexpireメソッドは、abstractではなくstaticが付いているので、空実装にすることはできず、{ }で処理を定義しないとコンパイルエラーとなります。
最後に、Shipクラスですが、departメソッドの中でAnchorクラスが定義されています。
これは、ローカル・クラスといわれるもので、メソッドの中でのみ使用するクラスを定義する場合に使用し、問題なくコンパイルは成功します。
次のコードを確認してください。
interface ParentIF {
void
parentMetnod();
}
@FunctionalInterface
interface FuncIF {
// line 1
public default
void method() { } // line 2
// line 3
}
FuncIFインタフェースを正常にコンパイルするために必要な修正はどれですか?
1.
line 1を以下のように修正する。
interface
FuncIF extends ParentIF {
2.
line 2をコメントアウトする。
3.
line 3に以下のコードを挿入する。
public
static void classMethod() { }
4.
line 3に以下のコードを挿入する。
public
boolean equals(Object obj);
解答: 1
解説:
JavaSE8から導入された関数型インタフェースのルールに関する問題です。
@FunctionalInterfaceアノテーションは、そのインタフェースが関数型インタフェースの要件を満たしているかをコンパイル時に検出するためのアノテーションです。
関数型インタフェースの要件は、以下のとおりです。
・抽象メソッドがひとつのみ定義されていること
・staticメソッド、defaultメソッドは、抽象メソッドとしてカウントされない(つまり、あってもなくても構わない)
・Objectクラスに定義されているメソッドについても、抽象メソッドとしてカウントされない(どのクラスにも間接的に必ず実装されているため)
上記の要件を満たすためには、ParentIFインタフェースにある抽象メソッドparentMetnodをインタフェースFuncIFに継承する必要があるため、選択肢1が正解となります。
次のコードを確認してください。
Deque<Integer> nums = new ArrayDeque<>();
nums.push(10);
nums.add(20);
nums.push(30);
System.out.println(nums.remove());
System.out.println(nums.pop());
System.out.println(nums);
このコードをコンパイル、実行すると何が出力されますか。
1.
30
10
[20]
2.
30
10
[10,
20]
3.
10
20
[30]
4.
10
20
[20,
30]
解答: 1
解説:
Dequeインタフェースに関する問題です。
Dequeインタフェースは、「double ended queue」(両端キュー)の省略形であり、通常は「デック」と発音されます。
このインタフェースでは、その名の通り先頭と末尾の両端に対して要素を追加したり取得したりするメソッドが提供されています。
まず、要素の追加メソッドですが、pushメソッドは先頭に、addメソッドは末尾に要素を追加していきます。
つまり、nums.push(30);までを実行し終えた時点で、numsの中身は[30, 10, 20]という状態になっています。
次に要素の取得メソッドですが、removeもpopも先頭から要素を取得し、それと同時に削除します。
そのため、nums.remove()が30を返し、nums.pop()が10を返すこととなり、最後にnumsの中身[20]が表示されることになります。
Dequeインタフェースは、QueueインタフェースやCollectionインタフェースを継承しているために、似た用途のメソッドが複数定義されて複雑になっています。
ポイントとしては、要素の追加と取得の場所(先頭なのか末尾なのか)、また要素の取得後にその要素を削除するのかしないのかといったところを正確に覚えておくようにしてください。
次のコードを確認してください。
class Shoes {
private String
color;
public
Shoes(String color) { this.color = color; }
public String
getColor() { return color; }
}
public class Test {
public static
void main(String[] args) {
List<Shoes> sList = Arrays.asList(
new
Shoes("Blue"),
new
Shoes("Red"),
new
Shoes("Green")
);
Stream<Shoes> ss = sList.stream();
// line 1
System.out.println(count);
}
}
このコードをコンパイル、実行して2が表示されるようにするには、どのコードをline 1に挿入しますか。
1.
long count =
ss.map(s -> s.length() > 3)
.count();
2.
long count =
ss.map(s -> s.getColor())
.peek(s
-> s.length() > 3)
.count();
3.
long count =
ss.map(Shoes::getColor)
.filter(s
-> s.length() > 3)
.count();
4.
long count =
ss.map(s -> s.getColor().subSequence(0,1))
.count();
解答: 3
解説:
ストリームAPIに関する問題です。
ストリームは、コレクション・オブジェクトなどからstreamメソッドを使って生成します。
まず、すべての選択肢にあるmapメソッドですが、これはストリームの各要素を変換したものを要素として持つあらたなストリームを返します。
選択肢1については、mapメソッドに記述しているラムダ式で表される引数 s が、Shoesオブジェクトとなりlengthメソッドを呼び出すことはできないのでコンパイルエラーとなります。
選択肢2については、mapメソッドでShoesオブジェクトからStringオブジェクトのストリームに変換するところまでは良いのですが、peekメソッドで渡せる引数がConsumer型(つまり、ラムダ式で引数は不要)のためコンパイルエラーとなります。
選択肢3が正解ですが、これはmapメソッドでShoesオブジェクトをStringオブジェクト(color)に変換したあと、filterメソッドで文字列の長さが3を超えるものだけ選択しています。
"Blue"と"Green"が条件に対してtrueとなるため、最後のcountメソッドで2が返ってきます。
なお、Shoes::getColorはメソッド参照という指定の仕方で、変換にShoesクラスのgetColorメソッドを使用します。
選択肢4は、Shoesオブジェクトのcolorの先頭1文字を切り取ったかたちのStringストリームに変換され、それに対してcountメソッドを呼んでいるので、3が返ってきてしまいます。
次のコードを確認してください。
List<Integer> list = Arrays.asList(3, 3, 3,
null); // line 1
Function<Integer, Integer> func = i -> i; // line 2
Double avg =
// line 3
list.stream()
.mapToInt(func) // line 4
.average(); // line 5
System.out.println(avg);
このコードをコンパイル、実行して3.0と表示するために必要な修正はどれですか。(3つ選択してください)
1.
line 1を以下のように書き換える。
List<Integer>
list = Arrays.asList(3, 3, 3, 3);
2.
line 2を以下のように書き換える。
ToIntFunction<Integer>
func = i -> i;
3.
line 3を以下のように書き換える。
double
avg =
4.
line 4を以下のように書き換える。
.map(func)
5.
line 5を以下のように書き換える。
.average().orElse(0.0);
解答: 1,2,5
解説:
プリミティブを扱うストリーム、およびOptionalオブジェクトの扱いに関する問題です。
まず、ストリームのソースとなるlistオブジェクトですが、あとの流れで要素に対してパイプライン操作をする際にnullが含まれているとNullPointerExceptionが発生していまうため、要素内にnullが混在しないようにします。(選択肢1)
また、line 5で実行するaverageメソッドは、IntStreamインタフェースのメソッドなので、line 4のようにしてmapToIntでIntStreamに変換する必要があります。(選択肢4は間違い)
mapToIntメソッドには、引数としてToIntFunction型を渡す必要があるため、line 2はToIntFunctionにします。(選択肢2)
最後に、averageメソッドは戻り値としてOptionalDoubleオブジェクトを返すため、getAsDoubleやorElseなどのメソッドを用いて値を取得する必要があります。(選択肢5)
なお、OptionalDoubleから取得したdouble値は、オートボクシングによってDouble型の変数に代入することができるため、選択肢3のような修正は不要です。
次のコードを確認してください。
BiFunction<String, Double, Integer> bf = (s, d)
-> s.length() * d; // line 1
System.out.println(bf.apply("Java", 0.5)); // line 2
このコードをコンパイル、実行すると何が出力されますか。
1.
2
2.
2.0
3.
line 1でコンパイルエラーが発生する。
4.
line 2でコンパイルエラーが発生する。
解答: 3
解説:
java.util.functionパッケージのインタフェースと、オートボクシング/アンボクシングについての問題です。
java.util.functionパッケージにあるインタフェースについては、内部でオートボクシング/アンボクシングが行われるため、例えば次のようなコードも正常に動作します。
Function<String, Integer> func = s ->
s.length();
System.out.println(func.apply("Java"));
(結果は、4が出力される)
ただし、オートボクシング/アンボクシングは、あくまでプリミティブ型とそのラッパー・クラス間での変換のため、double型からInteger型に変換するようなことはできません。
line 1で定義しているラムダ式の処理、s.length() * dの部分に"Java"と0.5を渡すと、結果はdouble型になるため、BiFunctionの3つ目のジェネリクスで指定されたInteger型には変換できません。
よって、選択肢3のとおりline 1でコンパイルエラーが発生します。
ちなみに、
BiFunction<String, Double, Double> bf = (s, d)
-> s.length() * d;
もしくは
BiFunction<String, Double, Integer> bf = (s, d)
-> (int)((double)(s.length()) * d);
とすれば、コンパイルおよび実行ともに成功するようになります。
次のコードを確認してください。
Optional<String> str = Optional.of(null); // line 1
System.out.println(str.orElse(null)); // line 2
System.out.println(str.get()); // line 3
System.out.println(str.isPresent()); // line 4
str.ifPresent(System.out::println); // line 5
このコードをコンパイルして実行したとき、どの行で実行時エラーが発生しますか。(最初に発生する行を選択してください)
1.
line 1
2.
line 2
3.
line 3
4.
line 4
5.
line 5
解答: 1
解説:
JavaSE8で導入されたOptionalクラスに関する問題です。
Optionalオブジェクトは、staticメソッドのofメソッドもしくはofNullableメソッドを使用して生成します。(Optional.empty()で、空のOptionalインスタンスを生成することもできます)
line 1では、ofメソッドを使用してOptionalオブジェクトを生成していますが、このメソッドにはnullを引数として渡すことはできない(NullPointerException発生)ため、ここで実行時例外が発生します。
ちなみに、line 1をofNullableと修正すると、次はline 3でNullPointerExceptionが発生するようになります。
それ以外は、実行時例外が発生する可能性がある部分はありません。
次のコードを確認してください。
class City {
String name;
int ward;
public
City(String name, int ward) {
this.name =
name;
this.ward =
ward;
}
public String
getName() { return name; }
public int
getWard() { return ward; }
public String
toString() { return name + ":" + ward; }
}
public class Test {
public static
void main(String[] args) {
List<City> cities = Arrays.asList(
new
City("Yokohama", 18),
new
City("Hamamatsu", 7),
new
City("Nagoya", 16),
new
City("Fukuoka", 7)
);
cities.stream().sorted(
Comparator.comparing(City::getWard)
.thenComparing(City::getName)
.reversed()
).forEach(System.out::println);
}
}
このコードをコンパイル、実行すると何が出力されますか。
1.
コンパイルエラーが発生する。
2.
Fukuoka:7
Hamamatsu:7
Nagoya:16
Yokohama:18
3.
Yokohama:18
Nagoya:16
Fukuoka:7
Hamamatsu:7
4.
Yokohama:18
Nagoya:16
Hamamatsu:7
Fukuoka:7
解答: 4
解説:
Comparatorインタフェースに関する問題です。
ストリームのSortedメソッドは、引数として指定されたComparatorオブジェクトのロジックに基づき、ストリーム内の要素を並び替えます。
また、Comparatorインタフェースのstaticメソッドを使用して、Comparatorオブジェクトを生成することができます。
comparingメソッドは、引数としてFunction型を取り、そのFunction型操作の戻り値となる値をもとに自然順序付けで並び替えを行うComparatorオブジェクトを返します。
設問では、返ってきたComparatorオブジェクトに対して、さらに連鎖的にthenComparingやreversedを呼んで追加の並べ替えロジックを適用したComparatorオブジェクトを生成しています。
まとめると、以下のようになります。
comparing(City::getWard) … Cityオブジェクトのwardの数で並べ替え
thenComparing(City::getName) … wardの数で並べ替えたものについて、同じwardのものはさらにnameでアルファベット順に並べ替える
reversed() … 全体の並べ替えロジックを逆順にする
よって、ward数の7, 16, 18で並べ替えられたもののうち、同じward数のHamamatsuとFukuokaが名前順で並べ替えられ、最後に全体が逆順となるような並べ替えになります。
次のコードを確認してください。
class TextResource implements Closeable { // line 1
public void
close() throws IOException { }
}
public class Test {
public static
void main(String[] args) {
TextResource tr; // line
2
try (tr =
new TextResource()) { // line 3
// line
4
} catch
(IOException e) { }
// line 5
}
}
このコードをコンパイル、実行するために必要な修正はどれですか?
1.
line 1を以下のように変更する。
class
TextResource implements AutoCloseable {
2.
line 2を削除し、line 3を以下のように変更する。
try
(TextResource tr = new TextResource()) {
3.
line 4に以下のコードを挿入する。
tr.close();
4.
line 5に以下のコードを挿入する。
finally
{ tr.close(); }
解答: 2
解説:
Closeableインタフェースとtry-with-resourcesに関する問題です。
この問題では、TextResourceクラスをline 3のtry-with-resources構文で使用しています。
try-with-resourcesで指定できるクラスは、AutoCloseableインタフェースもしくはその継承インタフェースであるCloseableインタフェースを実装している必要があります。
TextResourceクラスは、既にCloseableインタフェースを実装しているので、選択肢1のようにAutoCloseableインタフェースに変更する必要はありません。
また、選択肢3や4のように明示的にcloseメソッドを呼ぶ必要がないのがtry-with-resources構文の本来の目的なので、これらの選択肢も当てはまりません。
最後に残った選択肢2が必要な修正ということでこの問題の正解となります。
try-with-resources構文の中でcloseするオブジェクトは必ずtry( )の中で宣言しなければならないので、このように変数trをline 2のような場所で宣言するとコンパイルエラーとなります。
2017年は、マドリッド(スペイン)の夏時間は3月26日の午前2時に始まります。この結果、午前2時が午前3時になります。
次のコードを確認してください。
ZoneId zid = ZoneId.of("Europe/Madrid");
ZonedDateTime dtFrom = ZonedDateTime.of(
LocalDateTime.of(2017, 3, 26, 0, 0), zid);
ZonedDateTime dtTo = dtFrom.plusHours(3);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("H:mm - ");
System.out.print(formatter.format(dtTo));
System.out.println(
ChronoUnit.HOURS.between(dtFrom, dtTo));
このコードを実行すると、どのような結果になりますか。
1.
3:00 - 3
2.
3:00 - 4
3.
4:00 - 3
4.
4:00 - 4
解答: 3
解説:
JavaSE8から導入された日時APIに関する問題です。
日時APIについては、ZonedやOffsetが付くクラスの特徴についてもしっかりと把握しておくようにしてください。
特に、夏時間(サマータイム)に関する問題も出題されるため、そのルールを理解しておく必要があります。
(なお、夏時間の開始日・終了日や、何時が何時になるのかといった情報は、この設問のように問題文に明記されていますので、暗記するような必要はありません)
ZonedDateTimeオブジェクトで、夏時間を挟む時間操作を行った場合は、以下のようなルールになります。
・ChronoUnit.HOURS.betweenメソッドで算出した時間差:実際の時間差(現実世界で経過した時間と同じイメージ)
・DateTimeFormatterで出力した時刻:時計に表示されているのと同じ時刻
次のコードを確認してください。
try (FileInputStream fis = new
FileInputStream("Welcome.txt");
InputStreamReader
isr = new InputStreamReader(fis);
BufferedReader
br = new BufferedReader(isr)) {
System.out.print((char) br.read());
br.skip(2);
System.out.print((char) br.read());
br.mark(4);
System.out.print((char) br.read());
br.reset();
System.out.print((char) br.read());
} catch (IOException e) { }
Welcome.txtはアクセス可能で、次のような内容になっているとします。
JavaSE8
このコードを実行すると、どのような結果になりますか。
1.
Java
2.
Jv8v
3.
JaSS
4.
Jaa
解答: 3
解説:
ファイル入出力のストリームに関する問題です。
BufferedReaderクラスのreadメソッドは、ストリームから1文字を読み込んで現在位置を1文字進めます。
また、skipメソッドは引数で指定された文字数分、現在位置を進めます。
resetメソッドは、事前にmarkメソッドでマークした位置に読み込みの現在位置をリセットします。
(markメソッドの引数は、マークを保持しながら読み込むことができる文字数の上限を指定するが、この設問の場合は特に意味はありません)
この設問の場合は、以下のような処理になります。
1つ目のbr.read() … Jを読んで出力し、現在位置を次のaに進めます。
2文字skip
2つ目のbr.read() … skipメソッドでaとvがスキップされているため、4文字目のaを出力して現在位置をSに進めます
Sの位置でmark
3つ目のbr.read() … Sを出力し、現在位置をEに進めます。
resetして、Sの位置に戻る
4つ目のbr.read() … Sを出力し、現在位置をEに進めます。
よって、選択肢3のような出力になります。
次のコードを確認してください。
Path file1 =
Paths.get("/home/duke/./doc/abc.txt");
Path file2 =
Paths.get("/home/duke/doc/xyz.txt");
Path path1 = file2.relativize(file1);
Path path2 = file2.resolve("/web/page.html");
System.out.println(path1.getNameCount());
System.out.println(path2.getNameCount());
このコードを実行すると、どのような結果になりますか。
1.
2
4
2.
2
2
3.
5
4
4.
5
2
解答: 2
解説:
NIO.2のAPIのPathインタフェースに関する問題です。
ファイルやディレクトリのパスを表すPathオブジェクトは、Pathsクラスのstaticメソッドgetを使用して生成します。
relativizeメソッドは、引数で指定されたPathオブジェクトへの相対パスを返すのでpath1は../abc.txt になります。
このとき、カレント・ディレクトリを表す .(ドット)も解決されずに残るため、path1は../abc.txtになります。
また、resolveメソッドは、引数に指定されたパスを元のオブジェクトのパスに対して追加します。
ただし、引数で指定されたパスが絶対パスのときは、引数のパスがそのまま返ってくるため、path2は/web/page.htmlになります。
最後のgetNameCountメソッドは、パスの区切り文字(ここでは / )で区切られた要素の数を返すため、
../abc.txt … 2
/web/page.html … 2
が出力され、選択肢2が正解となります。
次のファイルが存在しており、アクセス可能であるとします。
/bucket/brick.txt
/bucket/glass/water.txt
次のコードも確認してください。
Stream<Path> stream1 =
Files.list(Paths.get("/bucket"));
stream1.forEach(System.out::println);
Stream<Path> stream2 =
Files.walk(Paths.get("/bucket"));
stream2.forEach(System.out::println);
ディレクトリ内にソフト・リンクもシンボリック・リンクもない場合、このコードを実行すると、どのような結果になりますか。
1.
bucket/brick.txt
bucket/glass
bucket/brick.txt
bucket/glass
2.
bucket/brick.txt
bucket/glass
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
3.
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
4.
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
bucket/brick.txt
bucket/glass
解答: 2
解説:
ストリームAPIのファイル操作に関する問題です。
Filesクラスのlistメソッドは、引数で指定されたパスの直下にあるファイルやディレクトリを要素に含むStreamオブジェクトを返します。
このとき、直下のディレクトリの、そのさらに下にある要素については含まれません。(再帰的ではありません)
Filesクラスのwalkメソッドは、引数で指定されたパスを開始点として、その下にあるファイルやディレクトリを再帰的にすべて含むStreamオブジェクトを返します。
このとき、開始点となるディレクトリも含まれるため、bucketディレクトリも含まれることになります。
以上をまとめると
・listメソッドのStreamオブジェクトに含まれる要素
bucket/brick.txt
bucket/glass
・walkメソッドのStreamオブジェクトに含まれる要素
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
となり、選択肢2が正解となります。
次のコードを確認してください。
class CTask implements Callable<String> {
public String
call() { return "Hola "; }
}
class RTask implements Runnable {
public void
run() { System.out.print("Ciao "); }
}
ExecutorService srv = Executors.newCachedThreadPool();
srv.submit(new CTask()); // line 1
srv.execute(new RTask()); // line 2
srv.shutdown();
Hola Ciaoと表示されるようにするには、どのように修正しますか。
1.
line 1を以下のように修正する。
Future<String>
ft = srv.execute(new CTask());
System.out.print(ft.get());
2.
line 1を以下のように修正する。
Future<String>
ft = srv.submit(new CTask());
System.out.print(ft.get());
3.
line 1を以下のように修正する。
System.out.println(srv.execute(new
CTask()));
4.
line 2を以下のように修正する。
srv.submit(new
RTask());
5.
修正の必要はない。
解答: 2
解説:
Callable、Future、ExecutorServiceといったマルチスレッド・プログラミングに関する問題です。
CTaskクラスは、Callableインタフェースを実装しているため、callメソッドの戻り値としてFutureオブジェクトが返ってきます。
Futureオブジェクトは、getメソッドでcallメソッドの戻り値を取得することができ、このとき呼び出し元のスレッドはブロックします。
また、Executorsクラスを使用して生成するExecutorServiceオブジェクトは、RunnableオブジェクトやCallableオブジェクトを内部で生成されたスレッドによって実行するものです。
この設問では、CallableのCTaskオブジェクトをExecutorServiceで実行していますが、戻り値を取得していないために"Hola"はそのままでは出力されません。
そこで、CTaskオブジェクトの戻り値をFutureオブジェクトとして取得しておき、getメソッドで返ってくる処理結果を出力するようにします。
また、ExecutorServiceにCallableオブジェクトを渡すメソッドは、submitメソッドを使用する必要があるため、選択肢2が正解となります。
次のコードを確認してください。
class Work implements Runnable {
public static
AtomicInteger count = new AtomicInteger(0);
public void
run() { // line 1
count.getAndIncrement(); // line
2
}
}
public class Test {
public static
void main(String[] args) throws Exception {
ExecutorService
srv = Executors.newCachedThreadPool();
Work wk =
new Work();
for (int
i=0; i<10; i++) {
srv.execute(wk);
}
srv.shutdown();
srv.awaitTermination(5, TimeUnit.SECONDS); // line 3
System.out.println(Work.count);
}
}
このコードを実行して必ず10と表示されるようにするためには、どのような修正が必要ですか。
1.
line 1を以下のように変更する。
public
synchronized void run() {
2.
line 2を以下のように変更する。
count.incrementAndGet();
3.
line 3を削除する。
4.
修正の必要はない。
解答: 4
解説:
ConcurrencyフレームワークのAtomicIntegerクラスに関する問題です。
AtomicIntegerクラスは、単一の変数に対してスレッド・セーフに値を更新する機能を提供するクラスです。
この設問の処理の内容としては、内部にAtomicIntegerオブジェクトを保持するWorkクラスがあり、RunnableインタフェースのrunメソッドでAtomicIntegerオブジェクトをインクリメントしています。(getAndIncrementメソッド)
これをExecutorServiceに10回追加していますが、AtomicIntegerオブジェクトがstaticであるため、共通の1つのオブジェクトに対して非同期に10回インクリメント処理がなされることになります。
もしAtomicIntegerではなくIntegerオブジェクトを使用してインクリメントした場合は、スレッドの切替えのタイミングによって正確に10回インクリメントされない可能性がありますが、ここではAtomicIntegerを使用しているため、必ず10回インクリメントされることが保証されます。
次のようになっているとします。
・必要なデータベース・ドライバはクラスパス内で設定されている。
・dbURL、userNameおよびpassWordでアクセスできる適切なデータベースが存在する。
・Person表には整数型のid列があり、SQL問合せは1つのレコードに一致する。
このとき、次のコードをコンパイルおよび実行すると、どのような結果になりますか。
String query = "SELECT * FROM Person WHERE id =
10";
try (Connection conn = DriverManager.getConnection(URL,
userName, passWord);
Statement stmt
= conn.createStatement();
ResultSet rs =
stmt.executeQuery(query)) {
System.out.println(rs.getInt("id")); // line 1
} catch (SQLException ex) {
System.out.println("Fail");
}
1.
10と表示される。
2.
Failと表示される。
3.
何も表示されない。
4.
line 1でコンパイルに失敗する。
解答: 2
解説:
JDBCのAPIに関する問題です。
ResultSetオブジェクトは、Statementオブジェクトに対してSQL文を実行した結果を保持するオブジェクトで、get系のメソッドで現在カーソルがある行のデータを取得することができます。
ただし、ResultSetのカーソルは初期状態では最初の行の前に位置付けられているため、最初にnextメソッドを呼んでおかないとSQLExceptionが発生してしまいます。
よって、line 1でgetIntメソッドを呼び出した際にSQLExceptionが発生し、Failと出力されます。
更新可能なResultSetオブジェクトrsを使用して新規レコードデータを挿入する場合、正しいコードは次のうちどれですか。
1.
rs.moveToInsertRow();
rs.updateInt("id",
35);
rs.updateString("name",
"Larry");
rs.insertRow();
2.
rs.moveToInsertRow();
rs.updateInt("id",
35);
rs.updateString("name",
"Larry");
rs.updateRow();
3.
rs.moveToInsertRow();
rs.insertInt("id",
35);
rs.insertString("name",
"Larry");
rs.insertRow();
4.
rs.insertRow();
rs.insertInt("id",
35);
rs.insertString("name",
"Larry");
解答: 1
解説:
JDBCのAPIに関する問題です。
更新可能なResultSetオブジェクトを使用して新規レコードデータを挿入する場合は、以下の手順になります。
1:moveToInsertRowで新規レコードの行に移動する。
2:update系のメソッドで各列に値を設定していく。
3:insertRowメソッドで新規レコードを挿入します。
よって、選択肢1が正解となります。
デフォルトのロケールが、国 : ドイツ、言語 : ドイツ語 に設定されているとします。
次のコードを確認してください。
Locale loc;
// line 1
System.out.println(loc);
de_DEと表示されないのは、次のうちどれですか?
1.
loc = new
Locale("de", "DE")
2.
loc =
Locale.GERMAN;
3.
loc =
Locale.getDefault();
4.
loc = new
Locale.Builder()
.setLanguage("de")
.setRegion("DE")
.build();
解答: 2
解説:
Localeクラスに関する問題です。
以下のいずれの方法でもLocaleオブジェクトを生成、取得することができます。
・コンストラクタによって生成する
・Localeクラスの定数を使用する
・LocaleクラスのgetDefaultメソッドでデフォルトのロケール設定から生成する
・Localeクラスの内部クラスであるBuilderクラスを使用して生成する
この中のLocaleクラスの定数については、以下の例のように言語だけのものと言語と国の組み合わせが用意されています。
Locale.ENGLISH … en(言語:英語)
Locale.UK … en_GB(言語:英語/国:イギリス)
特に、GERMAN(言語:ドイツ語)とGERMANY(言語:ドイツ語/国:ドイツ)の違いには注意が必要です。
設問のように、言語と国の両方が設定されているのはGERMANではなくGERMANYなので、選択肢2がdeとしか出力されず正解となります。