One: How to understand OOP programming ideas

Simple understanding:

  1. Class - >Copy the code
  2. Members --> Human attributes such as eyesCopy the code
  3. Methods --> Human attribute behavior, such as: eyes blink, tearsCopy the code

Two: basic knowledge

Handles manipulate objects

String s; // This creates an S handle, not an object
String s1 = new String("abc");// This creates an S1 handle that points to the new String(" ABC ") objectHandle data is stored in the: stack. Object data is stored in: heap.Copy the code

Where is the data stored

When the program runs, we should have a basic understanding of where the data is stored. In particular, memory allocation. There are six places to store data:

Register: This is the fastest save area, because unlike other save methods, it is located inside the processor. The number of registers is very limited, so registers are allocated by the compiler as needed. We have no direct control, nor can we find any trace of registers in our own programs.

Stack: Resides in the regular RAM (random-access memory) area, but can be directly supported for processing through its “stack pointer.” Moving the heap pointer down creates new memory; If you move it up, it frees that memory. This is an exceptionally fast and efficient way to store data, second only to registers. When you create a program, the Java compiler must know exactly how long and how long all the data is stored in the stack. This is because it must generate code to move the pointer up and down. This limitation definitely affects the flexibility of the program, so while some Java data is stored on the stack — especially object handles — Java objects are not.

Heap: A general-purpose memory pool (also in the RAM area) that holds Java objects. Unlike a stack, the most attractive aspect of a “memory Heap” or “Heap” is that the compiler does not have to know how much storage space to allocate from the Heap or how long the stored data will stay in the Heap. As a result, you get more flexibility when you store data in the heap. When you ask to create an object, you simply code it with the new command. As the code executes, the data is automatically saved in the heap. Of course, this flexibility comes at a price: it takes longer to allocate storage in the heap!

Static storage: “Static” here means “in a fixed location” (although it is also in RAM). While the program is running, statically stored data is always waiting to be invoked. The static keyword can be used to indicate that a particular element of an object is static. But Java objects themselves are never put into static storage.

Constant storage: Constant values are usually placed directly inside program code. It is safe to do so because they will never change. Sometimes constants

They need to be protected strictly, so consider placing them in read-only memory (ROM).

Non-ram storage: Data that is completely independent of a program can exist when the program is not running and is out of the control of the program. Two of the most important examples are “streaming objects” and “fixed objects.” For streaming objects, the object becomes a byte stream, which is usually sent to another machine. For fixed objects, objects are stored on disk. Even when programs are aborted, they retain their state. A particularly useful trick for these types of data stores is that they can exist in other media. They can even be restored to normal, RAM-based objects if needed.

Primary types (basic data types)

int i = 0;
String str = new String("a"); I don't know if you've ever thought about it, why not basic data typesnewI can create an object, right?Copy the code

There are a number of classes that need special treatment; Think of them as “Primitive,” “primary,” or “primary” types and use them frequently when programming. The reason for this special treatment is that creating objects (especially small, simple variables) with New is not very efficient because new puts objects in a heap. For these types, Java takes the same approach as C and C++. That is, instead of creating a variable with new, you create an “automatic” variable that is not a handle. This variable holds the specific value and is placed on the stack for more efficient access. Java determines the size of each major type. As in most languages, these sizes do not vary with the structure of the machine. This immutable size is one of the reasons Java programs are so portable.

Accuracy loss problem

Java provides two classes to solve this problem, sacrificing speed for precision: int to float, long to float, long to double

BigInteger supports integers of arbitrary precision. That is, we can accurately represent integer values of any size without losing any information in the process.

BigDecimal supports fixed-point numbers with arbitrary precision.

Initialization and cleanup

For method creation, think of it as calling Initialize () once for every class you write. The name reminds us that such a call should be made before using an object. Unfortunately, this also means that the user must remember to call the method. In Java, class designers can guarantee that every object will be correctly initialized, thanks to a special method called a “builder.”

Programmers know the importance of initialization, but often forget the importance of cleanup. After all, who needs to clear an int? But it’s not always safe for libraries to simply “free” an object when they’re done with it. Of course, Java can use the garbage collector to reclaim memory occupied by objects that are no longer used. Now consider a very special and rare case. Suppose our object allocates a “special” memory region and does not use new. The garbage collector only knows how to free the memory allocated by new, so it does not know how to free the object’s “special” memory. To solve this problem, Java provides a method called Finalize (), which can be defined for our class. Ideally, it would work like this: Once the garbage collector is ready to release the storage space that an object occupies, it first calls Finalize (), and only during the next garbage collection does it actually reclaim the object’s memory. So if you use Finalize (), you can do some important cleanup or sweep during garbage collection. Garbage collection is all about memory!

RTTI (run-time identification) and reflection

The only difference between RTTI and reflection is that for RTTI, the compiler opens and checks.class files at compile time. In other words, we can call all methods of an object in the “normal” way; For Reflection, however,.class files are not available at compile time, but are opened and examined by the runtime environment.

// Simple code
Student student = new Student();
User user = (User) student;// This is RTTI.

// reflection - to retrieve information that is not in this program, which is not detected by RTTI during compilation
 Class c = Class.forName("xxx/xx.java");
 Method[] m = c.getMethods();
 Constructor[] ctor = c.getConstructors();

Copy the code

Pass and return objects

All argument or parameter passing in Java is done by passing a handle. That is, when we pass “an object,” all we’re really passing is “a handle” to the object outside the method. So any modification to that handle is equivalent to modifying the external object. In addition:

  • Alias problems occur automatically during parameter passing
  • There are no local objects, only local handles
  • Handles have their own scope, while objects do not
  • The “lifetime” of an object is not an issue in Java
  • There is no language support (such as constants) to prevent objects from being modified (to avoid the side effects of aliases)

Clone object

If you want to modify an object without changing the caller’s object, make a local copy of the object. This is also one of the most common uses of local replicas. If you decide to make a local copy, simply use the clone() method. If you want a class to be cloned, then:

  1. Implement Cloneable interface
  2. Covering the clone ()
  3. Call super.clone() in your clone()
  4. Catch a violation in your own Clone ()
class Int2 implements Cloneable { private int i; public Object clone() { Object o = null; try { o = super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Int2 can't clone"); } return o; }}Copy the code

The Demo above refers to a simple clone, if it is to put the above class in a collection, and then clone the whole collection will need to do deep copy. As follows:

Vector v = new Vector(); for (int i = 0; i < 10; i++) { v.addElement(new Int2(i)); } Vector v2 = (Vector) v.clone(); //1 for (int i = 0; i < v.size(); i++) { v2.setElementAt(((Int2) v2.elementAt(i)).clone(), i); // If we do not clone(), the data handle stored in v2 will point to the same data handle as that stored in v, so the data will change regardless of whether we change v2 or VCopy the code

Of course, there are some security risks associated with cloning, so we can also eliminate cloning (turn cloning off) if we want a class to turn off cloning, then:

  1. Make the class final and do not implement the Cloneable interface

Read-only class

public class Immutable1 {
 private int data;
 public Immutable1(int initVal) {
 data = initVal;
 }
 public int read(a) { return data; }
 public boolean nonzero(a) { returndata ! =0; }
 public Immutable1 quadruple(a) {
 return new Immutable1(data * 4);
 }
 static void f(Immutable1 i1) {
 Immutable1 quad = i1.quadruple();
 System.out.println("i1 = " + i1.read());
 System.out.println("quad = " + quad.read());
 }
 public static void main(String[] args) {
 Immutable1 x = new Immutable1(47);
 System.out.println("x = " + x.read());
 f(x);
 System.out.println("x = "+ x.read()); }}// All data is set to private. You can see that there are no public methods to modify the data. In fact, you do need to modify an objectThe method is quadruple(), but that creates a new Immutable1 object, leaving the initial object untouched. Method f() takes an Immutable1 object and does something different to it, and the output from main() shows that no changes have been made to x. Therefore, the X object can be aliased many times without causing any harm, because the Immutable1 class is designed to keep the object unchanged.Copy the code

Object serialization functions:

  1. Interface objects that can be converted into a series of bytes and can be fully restored to their original form at a later date. This process can also be done over a network, which means the serialization mechanism automatically compensates for differences between operating systems. In other words, you can create an object on a Windows machine, serialize it, send it over the network to a Unix machine, and then reassemble it there without a mistake. You don’t have to worry about how the data is represented on different machines, byte order or any other details.

  2. It is possible to achieve “finite persistence,” which means that the “lifetime” of an object does not depend on whether the program is executing or not — it exists or “lives” between every invocation of the program. A “persistent” effect can be achieved by serializing an object, writing it to disk, and then restoring that object when the program is called again. It’s “limited” because you can’t simply define an object with some “persistent” keyword and have the system take care of all the other details automatically (though that might be possible in the future). Instead, you must serialize and assemble objects explicitly in your own programs.

  3. Serialization also provides support for RMI(remote method calls) and can also be used for cloning (although serialization for cloning is not as effective and unstable as the Clone () method)

Remotely invoke RMI

I believe that the invocation between services within the company is basically using “remote invocation technology”, such as Dubbo,Feign and other related technologies, but they have a common feature: “exposed in the form of interfaces”. So why is it implemented this way? This involves the concept of remote calls:

Remote interface concepts:

RMI has a strong dependency on interfaces. When we need to create a remote object, we hide the low-level implementation details by passing an interface. So when clients get a handle to a remote object, what they really get is an interface handle. This handle happens to be connected to some local root code, which is responsible for communicating over the network. But we don’t care about these things and just send the message through our interface handle. When creating a remote interface, the following rules must be followed:

(1) The remote interface must be public (no “package access”; That is, it can’t be “friendly”). Otherwise, the client will get an error if he tries to load a remote object that implements the remote interface.

(2) Remote interface must extend interface java.rmi.remote.

(3) Except for violations related to the application itself, each method in the remote interface must declare java.rmi.RemoteException in its own throws clause.

(4) A remote object passed as a parameter or return value (whether direct or embedded in a local object) must be declared as a remote interface and not as an implementation class.

Of course, it is important to note that the “remote call interface” method must have a default constructor for its input and return parameters, because what the client gets is a handle to the interface, not the class that implements it. You must explicitly define the builder for the remote object, even if you only want to define a default builder with which to invoke the underlying class builder. You have to write it explicitly.

—– Of course, there is much more to OOP than this: abstractions, interfaces, inheritance, IO, network programming, etc. However, I first recorded to this —–