Complete Works of Java Interview Questions (3)

Complete Works of Java Interview Questions (3)

Luo Hao IT haha

21. Describe the principle and mechanism of JVM loading class files?

Answer: The loading of classes in the JVM is implemented by the class loader (ClassLoader) and its subclasses. The class loader in Java is an important Java runtime system component, which is responsible for finding and loading classes at runtime The class in the file.
Due to the cross-platform nature of Java, the compiled Java source program is not an executable program, but one or more class files. When a Java program needs to use a certain class, the JVM will ensure that this class has been loaded, connected (verified, prepared, and parsed) and initialized. Class loading refers to reading the data in the class's .class file into memory, usually by creating a byte array to read into the .class file, and then generating the Class object corresponding to the loaded class. After loading, the Class object is not complete, so the class at this time is not yet available. When the class is loaded, it enters the connection phase. This phase includes three steps: verification, preparation (allocating memory for static variables and setting the default initial value) and resolution (replacement of symbol references with direct references). Finally, the JVM initializes the class, including: 1) If the class has a direct parent class and the class has not been initialized, then initialize the parent class first; 2) If there are initialization statements in the class, these initialization statements are executed in sequence.
The loading of classes is done by the class loader, which includes: root loader (BootStrap), extension loader (Extension), system loader (System) and user-defined class loader (java.lang.ClassLoader) Subclass). Starting from Java 2 (JDK 1.2), the parent delegation mechanism (PDM) has been adopted for the class loading process. PDM better guarantees the security of the Java platform. In this mechanism, the Bootstrap that comes with the JVM is the root loader, and other loaders have and only have one parent loader. The loading of the class first requests the parent class loader to load, and the child class loader will load it when the parent class loader cannot do anything. The JVM does not provide a reference to Bootstrap to Java programs. The following is a description of several class loaders:

  • Bootstrap: generally implemented with local code, responsible for loading the JVM basic core class library (rt.jar);
  • Extension: Load the class library from the directory specified by the java.ext.dirs system property, and its parent loader is Bootstrap;
  • System: Also called application class loader, its parent class is Extension. It is the most widely used class loader. It records classes from the directory specified by the environment variable classpath or the system property java.class.path, and is the default parent loader of the user-defined loader.

22. Can a Chinese character be stored in a char variable? Why?

Answer: The char type can store a Chinese character, because the encoding used in Java is Unicode (do not select any specific encoding, use the number of the character in the character set directly, this is the only unified method), a char type occupies 2 characters Section (16 bits), so it’s okay to put a Chinese.

Supplement: The use of Unicode means that characters have different manifestations inside and outside the JVM. Inside the JVM, they are all Unicode. When the character is transferred from the JVM to the outside (such as stored in the file system), encoding conversion is required. So there are byte streams and character streams in Java, as well as conversion streams that convert between character streams and byte streams, such as InputStreamReader and OutputStreamReader. These two classes are adapter classes between byte streams and character streams. The task of code conversion; for C programmers, to complete such code conversion I am afraid to rely on the characteristics of the union (union/union) shared memory to achieve.

23. What are the similarities and differences between abstract class and interface?

Answer: Abstract classes and interfaces cannot be instantiated, but references to abstract classes and interface types can be defined. If a class inherits an abstract class or implements an interface, all abstract methods in it need to be implemented, otherwise the class still needs to be declared as an abstract class. Interfaces are more abstract than abstract classes, because abstract classes can define constructors, abstract methods and concrete methods, but interfaces cannot define constructors and all of the methods are abstract methods. The members in the abstract class can be private, default, protected, and public, while the members in the interface are all public. Member variables can be defined in abstract classes, and member variables defined in interfaces are actually constants. Classes with abstract methods must be declared as abstract classes, and abstract classes do not necessarily have abstract methods.

24. What is the difference between Static Nested Class and Inner Class?

Answer: Static Nested Class is an internal class that is declared as static. It can be instantiated without relying on external class instances. The usual inner class needs to be instantiated after the outer class is instantiated, and the syntax looks very strange, as shown below.


/**
 * 扑克类(一副扑克)
 * @author 骆昊
 *
 */
public class Poker {
    private static String[] suites = {"黑桃", "红桃", "草花", "方块"};
    private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

    private Card[] cards;

    /**
     * 构造器
     * 
     */
    public Poker() {
        cards = new Card[52];
        for(int i = 0; i < suites.length; i++) {
            for(int j = 0; j < faces.length; j++) {
                cards[i * 13 + j] = new Card(suites[i], faces[j]);
            }
        }
    }

    /**
     * 洗牌 (随机乱序)
     * 
     */
    public void shuffle() {
        for(int i = 0, len = cards.length; i < len; i++) {
            int index = (int) (Math.random() * len);
            Card temp = cards[index];
            cards[index] = cards[i];
            cards[i] = temp;
        }
    }

    /**
     * 发牌
     * @param index 发牌的位置
     * 
     */
    public Card deal(int index) {
        return cards[index];
    }

    /**
     * 卡片类(一张扑克)
     * [内部类]
     * @author 骆昊
     *
     */
    public class Card {
        private String suite;   // 花色
        private int face;       // 点数

        public Card(String suite, int face) {
            this.suite = suite;
            this.face = face;
        }

        @Override
        public String toString() {
            String faceStr = "";
            switch(face) {
            case 1: faceStr = "A"; break;
            case 11: faceStr = "J"; break;
            case 12: faceStr = "Q"; break;
            case 13: faceStr = "K"; break;
            default: faceStr = String.valueOf(face);
            }
            return suite + faceStr;
        }
    }
}

Test code:


class PokerTest {
    public static void main(String[] args) {
        Poker poker = new Poker();
        poker.shuffle();                // 洗牌
        Poker.Card c1 = poker.deal(0);  // 发第一张牌
        // 对于非静态内部类Card
        // 只有通过其外部类Poker对象才能创建Card对象
        Poker.Card c2 = poker.new Card("红心", 1);    // 自己创建一张牌
        System.out.println(c1);     // 洗牌后的第一张
        System.out.println(c2);     // 打印: 红心A
    }
}

Interview Question-Where will the following code generate compilation errors?


class Outer {
    class Inner {}
    public static void foo() { new Inner(); }
    public void bar() { new Inner(); }
    public static void main(String[] args) {
        new Inner();
    }
}

Note: The creation of non-static internal class objects in Java depends on the external class objects. In the above interview questions, the foo and main methods are static methods. There is no this in the static method, which means that there is no so-called external class object. Create an inner class object. If you want to create an inner class object in a static method, you can do this:


    new Outer().new Inner();

25. Will there be a memory leak in Java? Please describe briefly.

Answer: Theoretically, because of the garbage collection mechanism (GC) in Java, there is no memory leakage problem (this is also an important reason why Java is widely used in server-side programming); however, in actual development, there may be useless but reachable Objects, these objects cannot be reclaimed by GC, so memory leaks can also occur. For example, the objects in hibernate's Session (first level cache) are in a persistent state, and the garbage collector will not reclaim these objects. However, there may be useless garbage objects in these objects. If they are not closed or flushed in time, The first level cache may cause memory leaks. The code in the following example will also cause memory leaks.


import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
    private T[] elements;
    private int size = 0;
    private static final int INIT_CAPACITY = 16;
    public MyStack() {
        elements = (T[]) new Object[INIT_CAPACITY];
    }
    public void push(T elem) {
        ensureCapacity();
        elements[size++] = elem;
    }
    public T pop() {
        if(size == 0) 
            throw new EmptyStackException();
        return elements[--size];
    }
    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

The above code implements a stack (first-in-last-out (FILO)) structure. At first glance, there seems to be no obvious problem. It can even pass the various unit tests you write. However, the pop method has a memory leak problem. When we pop an object in the stack with the pop method, the object will not be treated as garbage, even if the program using the stack no longer references these objects, because the stack is maintained internally Obsolete references to these objects. In languages ​​that support garbage collection, memory leaks are very hidden. Such memory leaks are actually unconscious object retention. If an object reference is unconsciously reserved, then the garbage collector will not process the object, nor will it process other objects referenced by the object, even if there are only a few such objects, it may cause many objects to be excluded In addition to garbage collection, it has a significant impact on performance. In extreme cases, Disk Paging (physical memory and virtual memory of hard disk exchange data) will be triggered, and even OutOfMemoryError will be caused.

26. Can abstract methods be static at the same time, can they be native methods at the same time, and can they be modified by synchronized at the same time?

Answer: None. Abstract methods need to be overridden by subclasses, while static methods cannot be overridden, so the two are contradictory. Native methods are methods implemented by native codes (such as C code), while abstract methods are not implemented and contradictory. Synchronization is related to the implementation details of the method, and abstract methods do not involve implementation details, so they are contradictory.

27. Explain the difference between static variables and instance variables.

Answer: A static variable is a variable modified by the static modifier. It is also called a class variable. It belongs to a class and does not belong to any object of the class. No matter how many objects are created in a class, there is only one copy of the static variable in memory. ; Instance variables must depend on an instance, you need to create an object and then access it through the object. Static variables can be implemented to allow multiple objects to share memory.

Supplement: In Java development, there are usually a large number of static members in context classes and tool classes.

28. Is it possible to call a non-static method from within a static method?

Answer: No, static methods can only access static members, because the call of non-static methods must first create the object, and the object may not be initialized when calling the static method.

29. How to implement object cloning?

Answer: There are two ways:
  1). Realize the Cloneable interface and rewrite the clone() method in the Object class;
  2). Realize the Serializable interface, through the serialization and deserialization of the object to achieve cloning, you can achieve true deep cloning ,code show as below.


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
    private MyUtil() {
        throw new AssertionError();
    }
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);
        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
        // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
        // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
    }
}
下面是测试代码:
import java.io.Serializable;
/**
 * 人类
 * @author 骆昊
 *
 */
class Person implements Serializable {
    private static final long serialVersionUID = -9102017020286042305L;
    private String name;    // 姓名
    private int age;        // 年龄
    private Car car;        // 座驾
    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
}
/**
 * 小汽车类
 * @author 骆昊
 *
 */
class Car implements Serializable {
    private static final long serialVersionUID = -5713945027627603702L;
    private String brand;       // 品牌
    private int maxSpeed;       // 最高时速
    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public int getMaxSpeed() {
        return maxSpeed;
    }
    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    @Override
    public String toString() {
        return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
    }
}

class CloneTest {
    public static void main(String[] args) {
        try {
            Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
            Person p2 = MyUtil.clone(p1);   // 深度克隆
            p2.getCar().setBrand("BYD");
            // 修改克隆的Person对象p2关联的汽车对象的品牌属性
            // 原来的Person对象p1关联的汽车不会受到任何影响
            // 因为在克隆Person对象时其关联的汽车对象也被克隆了
            System.out.println(p1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Note: Cloning based on serialization and deserialization is not only deep cloning, but more importantly, through generic restriction, you can check whether the object to be cloned supports serialization. This check is done by the compiler, not at An exception is thrown at runtime. This solution is obviously better than using the clone method of the Object class to clone an object. It is always better to expose the problem at compile time than to leave the problem at runtime.

30. What is GC? Why is there a GC?

Answer: GC means garbage collection. Memory processing is a place where programmers are prone to problems. Forgetting or wrong memory collection can cause instability or even crash of the program or system. The GC function provided by Java can automatically monitor whether the object exceeds the scope. So as to achieve the purpose of automatically reclaiming memory, the Java language does not provide a display operation method for releasing allocated memory. Java programmers don't need to worry about memory management, because the garbage collector will manage it automatically. To request garbage collection, you can call one of the following methods: System.gc() or Runtime.getRuntime().gc(), but the JVM can block the displayed garbage collection calls.
Garbage collection can effectively prevent memory leaks and effectively use available memory. The garbage collector usually runs as a separate low-priority thread. Under unpredictable circumstances, objects that have died or have not been used for a long time are cleared and recycled. The programmer cannot call the garbage collector in real time. An object or all objects are garbage collected. In the early days of Java, garbage collection was one of the biggest highlights of Java, because server-side programming needs to effectively prevent memory leaks. However, time has passed, and Java's garbage collection mechanism has now become a criticized thing. Mobile smart terminal users usually feel that the iOS system has a better user experience than the Android system. One of the deep-seated reasons lies in the unpredictability of garbage collection in the Android system.

Supplement: There are many garbage collection mechanisms, including: generational replication garbage collection, marked garbage collection, incremental garbage collection, etc. A standard Java process has both a stack and a heap. The stack holds primitive local variables, and the heap holds the objects to be created. The Java platform's basic algorithm for heap memory recovery and reuse is called marking and cleaning, but Java has improved it, using "generational garbage collection". This method divides the heap memory into different areas with the life cycle of the Java object. During the garbage collection process, the object may be moved to different areas:

  • Eden: This is the area where objects were first born, and for most objects, this is the only area where they have existed.
  • Survivor: Objects surviving from the Garden of Eden will be moved here.
  • Tenured: This is the home of survivors who are old enough. The Minor-GC process will not touch this place. When the young generation collection cannot put the object in the lifelong summer residence, a complete collection (Major-GC) will be triggered, and compression may also be involved here to make enough space for large objects.

JVM parameters related to garbage collection:

  • -Xms / -Xmx — initial size of the heap / maximum size of the heap
  • -Xmn — The size of the young generation in the heap
  • -XX:-DisableExplicitGC — Let System.gc() have no effect
  • -XX:+PrintGCDetails — Print GC details
  • -XX:+PrintGCDateStamps — Print the timestamps of GC operations
  • -XX:NewSize / XX:MaxNewSize — Set the young generation size/maximum size of the young generation
  • -XX:NewRatio — You can set the ratio of the old generation to the new generation
  • -XX:PrintTenuringDistribution — Set the age distribution of objects in the survivor paradise to be output after each new generation GC
  • -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold: set the initial value and maximum value of the threshold for the old generation
  • -XX:TargetSurvivorRatio: Set the target usage rate of the survival area

Guess you like

Origin blog.51cto.com/15061944/2593360