I have been in touch with Android development for some time. Recently, I began to find time to sort out some knowledge points, so as to reduce the time cost of repeated learning and improve my efficiency by sorting out notes.
At present, it first summarizes some Knowledge points of Java, which is the main content of this article to share. I want to specially state that this summary is more from my own programming basis and focus, so there will be selective neglect and focus in the content, there are many references to the blog and pictures, there is no way to list one by one, if there are improper quotes will be deleted immediately, I hope you forgive me. The list of knowledge points can be viewed in the right sidebar.
In addition, the knowledge points that will be sorted out later will also include Android SDK, Android source code, some other computer basics and common interview questions, etc. In the next month, I will add and update them one after another. I have created a project on Github, and I want to pay attention to welcome Star.
- Android Review materials
- Android Review materials — Android knowledge summary (1)
jvm
JVM Workflow
Runtime Data Area
area | instructions |
---|---|
Program counter | Each thread needs to have a program counter that records the address of the instruction being executed. If the Natvie method is being executed, this counter is null (Undefined). |
Java virtual machine stack | Java method execution memory model, each method execution, a stack frame is created to hold local variable table, operand stack, dynamic link, method exit information, etc. The process of a method call is the process of a stack frame going from VM to VM |
Local method stack | Much like the VM stack, which performs Java method (bytecode) services, the Native method stack performs Native method services. |
The Java heap | The sole purpose of this memory area is to hold object instances, and almost all objects are allocated memory there |
Methods area | The method area is the memory space shared by each memory. The method area mainly stores the class information loaded by JVM, constants, static variables, code compiled immediately and other data |
Method of instruction
instruction | instructions |
---|---|
invokeinterface | To invoke interface methods |
invokevirtual | Directives are used to invoke instance methods of objects |
invokestatic | To call class/static methods |
invokespecial | Use to call instance methods that require special handling, including instance initializer methods, private methods, and superclass methods |
Class loader
Class loader | instructions |
---|---|
BootstrapClassLoader | The Bootstrap class loader is responsible for loading JDK class files in rt.jar and is the parent of all class loaders. The Bootstrap class loader no parent class loader,. If you call the String class. GetClassLoader (), returns null, any code based on this will throw a NUllPointerException. The Bootstrap loader is called the initial class loader |
ExtClasssLoader | Extension will delegate the request to load the class to its parent loader, Bootstrap, and then load the class from the jre/lib/ext directory or the directory defined by the java.ext.dirs system property if the load fails. The Extension loader is implemented by sun.misc.Launcher$ExtClassLoader |
AppClassLoader | The third default loader is the System class loader (also known as the Application class loader). It is responsible for loading some application-specific classes from the CLASspath environment variable, which is usually defined by the -classpath or -cp command-line options, or the CLASspath attribute of the Manifest in the JAR. The Application class loader is a child of the Extension class loader |
The working principle of | instructions |
---|---|
Trust mechanism | The loading task is delegated to the parent class loader. If it fails, the delegate task is passed down and loaded by its subclass loader to ensure the security of the Java core library |
Visibility mechanism | The subclass loader can see the classes loaded by the parent class loader, but not vice versa |
Single mechanism | A class that has been loaded by the parent loader cannot be loaded a second time by the quilt loader |
static
- Methods or variables modified by the static keyword do not need to rely on the object to access, as long as the class is loaded, can be accessed by the class name.
- A static variable is shared by all objects and has only one copy in memory, which is initialized if and only if the class is first loaded.
- Can static member variables be accessed through this? All static methods and variables can be accessed through objects (as long as access is sufficient).
- Static is not allowed to modify local variables
final
- You can declare member variables, methods, classes, and local variables
- Final member variables must be initialized at declaration time or in the constructor, otherwise a compilation error will be reported
- The final variable is read-only
- Methods declared by final cannot be overridden by methods of subclasses
- Final classes are usually fully functional and cannot be inherited
- Final variables can be safely shared in multi-threaded environments without additional synchronization overhead
- The final keyword improves performance. Both JVM and Java applications cache final variables and optimize methods, variables, and classes
- The inner class of a method accesses local variables in the method, but must be decorated with final
String, StringBuffer, StringBuilder
- String is final and cannot be inherited. To change the value of an existing Stirng object is to create a new object
- A StringBuffer is a Stringbuffer similar to a String, whose value is modified using the append() method and converted to a String using the toString() method, which is thread-safe
- StringBuilder is used as an alternative to StringBuffer, StringBuilder is thread-safe and faster
Exception handling
- Exception and Error are subclasses of the Throwable class
- Error objects are generated and thrown by the Java VIRTUAL machine and cannot be captured
- The code in finally executes with or without exceptions
- When there is a return ina try or catch, the code in finally continues to execute
Common Error | ||
---|---|---|
OutOfMemoryError | StackOverflowError | NoClassDeffoundError |
Common Exception | ||
---|---|---|
Common non-checking abnormalities | ||
ArithmeticException | ArrayIndexOutOfBoundsException | ClassCastException |
IllegalArgumentException | IndexOutOfBoundsException | NullPointerException |
NumberFormatException | SecurityException | UnsupportedOperationException |
Common diagnostic abnormalities | ||
IOException | CloneNotSupportedException | IllegalAccessException |
NoSuchFieldException | NoSuchMethodException | FileNotFoundException |
The inner class
- Inner classes provide better encapsulation by hiding inner classes inside outer classes and denying access to them to other classes in the same package.
- Methods of the inner class have direct access to all data of the outer class, including private data.
polymorphism
- A reference to a parent class can point to an object of a subclass
- When a subclass object is created, the method called is the method overridden or inherited by the subclass
- If we write a unique method in a subclass, that method cannot be called from a subclass object created by a reference to the parent class
Abstractions and interfaces
- An abstract class cannot have an object (the new keyword cannot be used to create an object of an abstract class)
- Abstract methods in abstract classes must be overridden in subclasses
- All attributes in the interface default to: public static final ****;
- All methods in the interface default to: public abstract ****;
A collection of
- The List interface stores a Set of non-unique, ordered (insert order) objects, while the Set interface stores a Set of unique, unordered objects.
- A HashMap is non-synchronized and performs better. A HashMap can accept null keys and values, whereas a Hashtable is thread-safe and slower than a HashMap and does not accept NULL
reflection
try {
Class cls = Class.forName("com.jasonwu.Test");
// Get the constructor
Constructor[] publicConstructors = cls.getConstructors();
// Get all constructors
Constructor[] declaredConstructors = cls.getDeclaredConstructors();
// Get the public method
Method[] methods = cls.getMethods();
// Get all methods
Method[] declaredMethods = cls.getDeclaredMethods();
// Get public attributes
Field[] publicFields = cls.getFields();
// Get all attributes
Field[] declaredFields = cls.getDeclaredFields();
Object clsObject = cls.newInstance();
Method method = cls.getDeclaredMethod("getModule1Functionality");
Object object = method.invoke(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Copy the code
The singleton
The hungry type
public class CustomManager {
private Context mContext;
private static final Object mLock = new Object();
private static CustomManager mInstance;
public static CustomManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new CustomManager(context);
}
returnmInstance; }}private CustomManager(Context context) {
this.mContext = context.getApplicationContext(); }}Copy the code
Double check mode
public class CustomManager {
private Context mContext;
private volatile static CustomManager mInstance;
public static CustomManager getInstance(Context context) {
// Avoid unnecessary locking
if (mInstance == null) {
synchronized (CustomManger.class) {
if (mInstance == null) {
mInstacne = newCustomManager(context); }}}return mInstacne;
}
private CustomManager(Context context) {
this.mContext = context.getApplicationContext(); }}Copy the code
Static inner class schema
public class CustomManager{
private CustomManager(a){}
private static class CustomManagerHolder {
private static CustomManager INSTANCE = new CustomManager();
}
public static CustomManager getInstance(a) {
returnCustomManagerHolder.INSTANCE; }}Copy the code
Static inner classes work like this:
When a SingleTon is loaded for the first time, there is no need to load the SingleTonHoler. INSTANCE is initialized only when the getInstance() method is called for the first time. This method not only ensures thread-safety, but also ensures the uniqueness of singletons, while delaying the instantiation of singletons. The getInstance() method does not go to the new object more than once; it always takes the same INSTANCE object.
The virtual machine ensures that a class’s < Clinit >() methods are locked and synchronized correctly in a multithreaded environment. If multiple threads initialize a class at the same time, only one thread will execute the class’s < Clinit >() methods, and all the other threads will block until the active thread completes executing the < Clinit >() methods
The disadvantage is that you cannot pass parameters such as Context
thread
valatile
When a variable is declared volatile, both the compiler and the runtime notice that the variable is shared and therefore do not reorder operations on it with other memory operations. Volatile variables are not cached in registers or hidden from other processors. The JVM ensures that each read is read from memory, skipping the CPU cache, so volatile variables always return the most recently written value.
When a variable is defined as volatile, it has the following properties:
- This variable is guaranteed to be visible to all threads. It is not guaranteed to be atomic.
- Disallow instruction reordering optimization
- Volatile has almost the same read cost as normal variables, but writes are slower because it requires inserting many memory-barrier instructions into the native code to keep the processor from executing out of order
AtomicInteger mainly implements atomic operations of integers to prevent abnormal results in concurrent situations. Its internal implementation relies on the Unsafe class in the JDK to manipulate data in memory. The volatile modifier ensures that the value is visible to other threads in memory that it is worth changing. The CAS operation ensures that AtomicInteger can safely modify the value of value.
HashMap
How HashMap works
HashMap is based on hashing, where we store and retrieve objects using the put() and get() methods. When we pass the key-value pair to the put() method, it calls the key object’s hashCode() method to compute the hashCode, and then finds the bucket location to store the Entry object. A ‘collision’ occurs when two objects have the same Hashcode and their bucket positions are the same. Because HashMap uses a linked list to store objects, this Entry is stored in the linked list, and when the object is fetched, the correct key-value pair is found through the equals() method of the key object, and the value object is returned.
What if the size of the HashMap exceeds the capacity defined by the Load factor? The default load factor is 0.75, which means that when a map is 75% full of buckets, as with other collection classes such as ArrayList, an array of buckets twice the size of the original HashMap will be created to resize the map. And put the original object into the new bucket array. This process is called rehashing because it calls the hash method to find the new bucket location.
Why are wrapper classes like String and Interger good for keys? Because strings are immutable and final, and the equals() and hashCode() methods have been overridden. Other Wrapper classes have this feature as well. Immutability is necessary because in order to evaluate hashCode(), the key is prevented from changing, and if the key returns a different hashCode when it is put in and when it is retrieved, then the object you want cannot be found in the HashMap. Immutability has other advantages such as thread safety. If you can guarantee that hashCode is immutable simply by declaring a field final, do so. Because the equals() and hashCode() methods are used to get objects, it is important that the key object overrides them properly. If two objects that are not equal return different Hashcodes, there is less chance of collisions, thus improving the performance of the HashMap.
synchronized
When it is used to decorate a method or a code block, it ensures that at most one thread is executing the code at a time.
In Java, each object has a Monitor object, which is essentially a lock on the Java object, often referred to as a “built-in lock” or “object lock.” Class can have more than one object, so each object has its own object lock that does not interfere with each other. There is also a lock for each Class, which can be called a “Class lock.” Class locks are actually implemented through object locks, the Class object locks of classes. Each Class has only one Class object, so each Class has only one Class lock.
Monitor is a thread-private data structure, and each thread has a list of available Monitor Records, as well as a global list of available records. Each locked object is associated with a Monitor, and an Owner field in the monitor stores the unique identity of the thread that owns the lock, indicating that the lock is occupied by the thread. Monitor is a thread synchronization that relies on the underlying operating system’s Mutex Lock.
Sort by lock acquired
Obtaining the object lock
- synchronized(this|object) {}
- Decorates non-static methods
Get kind of lock
- Synchronized (class. The class) {}
- Decorated static methods
The principle of
Sync code block:
- Monitorenter and Monitorexit directives
Synchronized methods
- ACC_SYNCHRONIZED implementation on method modifiers
Lock
Pessimistic lock, optimistic lock
Pessimistic locks assume that data must be modified by other threads when they are using data. Therefore, they lock data before acquiring data to ensure that data cannot be modified by other threads. In Java, the implementation classes for the synchronized keyword and Lock are pessimistic locks. Pessimistic locking applies to scenarios where many write operations are performed. The pessimistic locking ensures correct data during write operations.
Optimistic locks, on the other hand, assume that no other thread will modify the data when they use it, so they will not add the lock, but just check whether the data has been updated before by another thread. If the data is not updated, the current thread writes its modified data successfully. If the data has already been updated by another thread, different operations are performed (such as reporting an error or automatic retry) depending on the implementation. Optimistic locking is implemented in Java by using lock-free programming, most commonly CAS algorithm, where incremental operations in Java atomic classes are implemented by CAS spin. Optimistic lock is suitable for scenarios where many read operations are performed. The no-lock feature greatly improves the read operation performance.
Spin lock, adaptive spin lock
Blocking or waking up a Java thread requires the operating system to switch CPU state, which takes processor time. If synchronizing the contents of a code block is too simple, state transitions can take longer than user code execution.
In many scenarios, synchronization resources are locked for a short period of time, and the cost of thread suspension and site recovery may not be worth the cost to the system to switch threads for this short period of time. If the physical machine has multiple processors that allow two or more threads to execute in parallel at the same time, we can let the later thread that requests the lock not give up CPU execution time to see if the thread that holds the lock will release the lock soon.
In order for the current thread to “hold on”, we need to spin the current thread. If the thread that held the synchronized resource before spinning has released the lock, the current thread can directly acquire the synchronized resource without blocking, thus avoiding the overhead of switching threads. This is the spin lock.
Spinlocks have their own drawbacks; they are no substitute for blocking. Spin waiting avoids the overhead of thread switching, but it takes up processor time. If the lock is held for a short period of time, the spin wait works very well. Conversely, if the lock is held for a long time, the spinning thread is a waste of processor resources. Therefore, the spin wait time must be limited, and the thread should be suspended if the lock is not successfully acquired if the spin exceeds the limit (the default is 10 spins, which can be changed using -xx :PreBlockSpin).
The implementation principle of spin locking is also CAS. In AtomicInteger, the source code calling unsafe for the increment operation, the do-while loop is a spin operation. If changing the value fails, the spin operation is performed through the loop until the change succeeds.
A deadlock
The current thread has resources needed by other threads, and the current thread waits for resources already owned by other threads without giving up its own resources.
That’s all for this share. If you like it, you can like 👍 or follow it. Feel free to point out any mistakes in the comments.
This article is personally original, please indicate the source of reprint.