• Shuffle
    Toggle On
    Toggle Off
  • Alphabetize
    Toggle On
    Toggle Off
  • Front First
    Toggle On
    Toggle Off
  • Both Sides
    Toggle On
    Toggle Off
  • Read
    Toggle On
    Toggle Off
Reading...
Front

Card Range To Study

through

image

Play button

image

Play button

image

Progress

1/12

Click to flip

Use LEFT and RIGHT arrow keys to navigate between flashcards;

Use UP and DOWN arrow keys to flip the card;

H to show hint;

A reads text to speech;

12 Cards in this Set

  • Front
  • Back

Describe and compare fail-fast and fail-safe iterators. Give examples.(3.26, 3.27, 3.29, 3.30Ok, 4.1OK)

The main distinction between fail-fast and fail-safe iterators is whether or not the collection can be modified while it is being iterated. Fail-safe iterators allow this; fail-fast iterators do not.Fail-fast iterators operate directly on the collection itself. During iteration, fail-fast iterators fail as soon as they realize that the collection has been modified (i.e., upon realizing that a member has been added, modified, or removed) and will throw a ConcurrentModificationException. Some examples include ArrayList, HashSet, and HashMap (most JDK1.4 collections are implemented to be fail-fast).Fail-safe iterates operate on a cloned copy of the collection and therefore do not throw an exception if the collection is modified during iteration. Examples would include iterators returned by ConcurrentHashMap or CopyOnWriteArrayList.

Why would it be more secure to store sensitive data (such as a password, social security number, etc.) in a character array rather than in a String?(3.26ok, 3.28ok)

In Java, Strings are immutable and are stored in the String pool. What this means is that, once a String is created, it stays in the pool in memory until being garbage collected. Therefore, even after you’re done processing the string value (e.g., the password), it remains available in memory for an indeterminate period of time thereafter (again, until being garbage collected) which you have no real control over. Therefore, anyone having access to a memory dump can potentially extract the sensitive data and exploit it.In contrast, if you use a mutable object like a character array, for example, to store the value, you can set it to blank once you are done with it with confidence that it will no longer be retained in memory.

What a ThreadLocal can do?


Talk about get() and set().


ThreadLocal instance & thread's id.


Holds implicit reference.


(3.29ok 3.30ok 4.1Ok 上机学习 4.6复习加写程序 4.8)

1.A single ThreadLocal instance can store different values for each thread independently.



2.Each thread that accesses the get() or set() method of a ThreadLocal instance is accessing its own, independently initialized copy of the variable.



3.ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or transaction ID). The example below, from the ThreadLocal Javadoc, generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.



public class ThreadId {


// Next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0);


// Thread local variable containing each thread's ID


private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); }}



Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

Explain volatile keyword.


Example.



(3.29ok 3.30ok)

1.In Java, each thread has its own stack, including its own copy of variables it can access. When the thread is created, it copies the value of all accessible variables into its own stack. The volatile keyword basically says to the JVM “Warning, this variable may be modified in another Thread”.



In all versions of Java, the volatile keyword guarantees global ordering on reads and writes to a variable. This implies that every thread accessing a volatile field will read the variable’s current value instead of (potentially) using a cached value.In Java 5 or later, volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.



Using volatile may be faster than a lock, but it will not work in some situations. The range of situations in which volatile is effective was expanded in Java 5; in particular, double-checked locking now works correctly.



2. One common example for using volatile is for a flag to terminate a thread. If you’ve started a thread, and you want to be able to safely interrupt it from a different thread, you can have the thread periodically check a flag (i.e., to stop it, set the flag to true). By making the flag volatile, you can ensure that the thread that is checking its value will see that it has been set to true without even having to use a synchronized block.



For example:public class Foo extends Thread { private volatile boolean close = false; public void run() { while(!close) { // do work } } public void close() { close = true; // interrupt here if needed }}

Compare the sleep() and wait() methods in Java, including when and why you would use one vs. the other.(3.29ok 3.31ok 4.1Ok 4.5Ok 还要上机研究)

sleep() is a blocking operation that keeps a hold on the monitor / lock of the shared object for the specified number of milliseconds.



wait(), on the other hand, simply pauses the thread until either (a) the specified number of milliseconds have elapsed or (b) it receives a desired notification from another thread (whichever is first), without keeping a hold on the monitor/lock of the shared object.



sleep() is most commonly used for polling, or to check for certain results, at a regular interval. wait() is generally used in multithreaded applications, in conjunction with notify() / notifyAll(), to achieve synchronization and avoid race conditions.

Tail recursion is functionally equivalent to iteration. Since Java does not yet support tail call optimization, describe how to transform a simple tail recursive function into a loop and why one is typically preferred over the other.



Example of typical recursion?


Example of typical tail recursion?


(3.30ok, 3.31, 4.2ok)

Here is an example of a typical recursive function, computing the arithmetic series 1, 2, 3…N. Notice how the addition is performed after the function call. For each recursive step, we add another frame to the stack.public int sumFromOneToN(int n) { if (n < 1) { return 0; } return n + sumFromOneToN(n - 1);}



Tail recursion occurs when the recursive call is in the tail position within its enclosing context - after the function calls itself, it performs no additional work. That is, once the base case is complete, the solution is apparent. For example:public int sumFromOneToN(int n, int a) { if (n < 1) { return a; } return sumFromOneToN(n - 1, a + n);}



Here you can see that a plays the role of the accumulator - instead of computing the sum on the way down the stack, we compute it on the way up, effectively making the return trip unnecessary, since it stores no additional state and performs no further computation. Once we hit the base case, the work is done - below is that same function, “unrolled”.



Many functional languages natively support tail call optimization, however the JVM does not. In order to implement recursive functions in Java, we need to be aware of this limitation to avoid StackOverflowErrors. In Java, iteration is almost universally preferred to recursion.


How can you catch an exception thrown by another thread in Java?(3.30Ok,3.31需要上机Ok, 还需上机学习理解)

Program

What is the Java Classloader? List and explain the purpose of the three types of class loaders.(3.30Ok, 4.1not pass, 4.5Ok)

The Java Classloader is the part of the Java runtime environment that loads classes on demand (lazy loading) into the JVM (Java Virtual Machine). Classes may be loaded from the local file system, a remote file system, or even the web.



When the JVM is started, three class loaders are used: 1. Bootstrap Classloader: Loads core java API file rt.jar from folder. 2. Extension Classloader: Loads jar files from folder. 3. System/Application Classloader: Loads jar files from path specified in the CLASSPATH environment variable.

When designing an abstract class, why should you avoid calling abstract methods inside its constructor?(3.30Ok, 3.31,4.3Ok)

This is a problem of initialization order. The subclass constructor will not have had a chance to run yet and there is no way to force it to run it before the parent class. Consider the following example class:



public abstract class Widget { private final int cachedWidth; private final int cachedHeight; public Widget() { this.cachedWidth = width(); this.cachedHeight = height(); } protected abstract int width(); protected abstract int height(); }



This seems like a good start for an abstract Widget: it allows subclasses to fill in width and height, and caches their initial values. However, look when you spec out a typical subclass implementation like so:


This is a problem of initialization order. The subclass constructor will not have had a chance to run yet and there is no way to force it to run it before the parent class. Consider the following example class:public abstract class Widget { private final int cachedWidth; private final int cachedHeight; public Widget() { this.cachedWidth = width(); this.cachedHeight = height(); } protected abstract int width(); protected abstract int height(); }This seems like a good start for an abstract Widget: it allows subclasses to fill in width and height, and caches their initial values. However, look when you spec out a typical subclass implementation like so:public class SquareWidget extends Widget { private final int size; public SquareWidget(int size) { this.size = size; } @Override protected int width() { return size; } @Override protected int height() { return size; } }Now we’ve introduced a subtle bug: Widget.cachedWidth and Widget.cachedHeight will always be zero for SquareWidget instances! This is because the this.size = size assignment occurs after the Widget constructor runs.


This is a problem of initialization order. The subclass constructor will not have had a chance to run yet and there is no way to force it to run it before the parent class. Consider the following example class:public abstract class Widget { private final int cachedWidth; private final int cachedHeight; public Widget() { this.cachedWidth = width(); this.cachedHeight = height(); } protected abstract int width(); protected abstract int height(); }This seems like a good start for an abstract Widget: it allows subclasses to fill in width and height, and caches their initial values. However, look when you spec out a typical subclass implementation like so:public class SquareWidget extends Widget { private final int size; public SquareWidget(int size) { this.size = size; } @Override protected int width() { return size; } @Override protected int height() { return size; } }Now we’ve introduced a subtle bug: Widget.cachedWidth and Widget.cachedHeight will always be zero for SquareWidget instances! This is because the this.size = size assignment occurs after the Widget constructor runs.



public class SquareWidget extends Widget { private final int size; public SquareWidget(int size) { this.size = size; } @Override protected int width() { return size; } @Override protected int height() { return size; } }



Now we’ve introduced a subtle bug: Widget.cachedWidth and Widget.cachedHeight will always be zero for SquareWidget instances! This is because the this.size = size assignment occurs after the Widget constructor runs.

Arrays.asList()


( 3.31Ok 4.2Ok)

fixed-size list backed by the specified array.


This method acts as bridge between array-based and collection-based APIs,


将一个数组转化为一个 fix sized List对象,这个方法会返回一个ArrayList类型的对象, 这个ArrayList类并非java.util.ArrayList类,而是Arrays类的静态内部类!用这个对象对列表进行添加删除更新操作,就会报UnsupportedOperationException异常

What variance is imposed on generic type parameters?


How much control does Java give you over this?examples()(4.2Ok 4.4复习上机ok)

Java’s generic type parameters are invariant. This means for any distinct types A and B, G<A> is not a subtype or supertype of G<B>. As a real world example, List<String> is not a supertype or subtype of List<Object>. So even though String extends (i.e. is a subtype of) Object, both of the following assignments will fail to compile:


List<String> strings = Arrays.<Object>asList("hi there"); List<Object> objects = Arrays.<String>asList("hi there");



Java does give you some control over this in the form of use-site variance. On individual methods, we can use ? extends Type to create a covariant parameter. Here’s an example:



public double sum(List<? extends Number> numbers) { double sum = 0; for (Number number : numbers) { sum += number.doubleValue(); } return sum; } List<Long> longs = Arrays.asList(42L, 128L, -10L); double sumOfLongs = sum(longs);



Even though longs is a List<Long> and not List<Number>, it can be passed to sum.



Similarly, ? super Type lets a method parameter be contravariant. Consider a function with a callback parameter: public void forEachNumber(Callback<? super Number> callback) { callback.call(50.0f); callback.call(123123); callback.call((short) 99); }



Note, however, that attempting to provide a callback that handles only Long (a subtype of Number) will rightly fail: // fails to compile! forEachNumber(new Callback<Long>() { ... });




Liberal application of use-site variance can prevent many of the unsafe casts that often appear in Java code and is crucial when designing interfaces used by multiple developers.





If one needs a Set, how do you choose between HashSet vs. TreeSet?


已上机学习。默写程


(4.7 )

At first glance, HashSet is superior in almost every way: O(1) add, remove and contains, vs. O(log(N)) for TreeSet.



However, TreeSet is indispensable when you wish to maintain order over the inserted elements or query for a range of elements within the set.