Basic java learning (6) combination and inheritance

Keywords: Java

By using combination and inheritance, new types can be generated from existing types to reuse code, instead of writing from scratch, so that classes can be used without destroying existing program code.

I. Combination
Only the objects of the existing classes need to be generated in the new classes. The new classes consist of the objects of the existing classes, which only reuse the functions of the existing program code, not its form.
Just place the object reference in the new class. For objects of non-primitive types, their references must be placed in new classes; however, primitive type data can be defined directly.

public class Springkler {
//Combining objects of various existing classes to generate new classes
    private String value;
    //You can initialize objects directly when they are defined
    private WaterSource waterSource=new WaterSource();
    //Basic types can be defined directly with default values
    private int i;
    public String toString(){
        return "String value="+value+"   "+
                "Object Value="+waterSource+"   "+
                "Integer value="+i+"   ";
    }

    public static void main(String[] args) {
        Springkler sk=new Springkler();
        System.out.println(sk.toString());
    }
}

class WaterSource{
    private String str;
    WaterSource(){
        str="Constructor";
    }
    public String toString(){
        return  str;
    }
}
//Result:
String value=null   Object Value=Constructor   
Integer value=0   

Every object of non-basic type has a toString() method, which is called when the compiler needs a String and you have only one object.

To avoid unnecessary burdens, the compiler does not create default objects for each reference. If you want to initialize these references, you can:
1. When defining objects. It can always be initialized before the constructor is called.
2. In the constructor of the class.
3. Initialization laziness can reduce the additional burden by initializing these objects before using them.
4. Use instance initialization.

public class Bath {
    //Initialization in place of object definition
    private  String value="This is a Test";
    private    Soap soup=new Soap();

    private  int i;
    Bath(int i){
        //Initialization in class constructor
        this.i=i;
    }

    public static void main(String[] args) {
        //Initialization before using these objects
        String str="test only ";
        System.out.println(str);
    }
}

class Soap{
    Soap(){
        System.out.println("Soup Constructor");
    }
}

II. Inheritance
When a class is created, it is always inherited, implicitly from Java's standard root class Object unless it is explicitly stated that it is inherited from other classes.

public class Detergent extends Cleanser{
    //Override scrub() method
    @Override
    public void scrub() {
        append("  Detergent.scrub()Method ");
        //Calling methods of base classes
        super.scrub();
    }

    public void form(){
        append("  form()  ");
    }

    public static void main(String[] args) {
        Detergent x=new Detergent();
        x.dilute();
        x.apply();
        x.scrub();
        x.form();
        System.out.println(x);
        //Direct call to main method in base class
        Cleanser.main(args);
    }
}

class Cleanser{
    private String s="Cleanser";
    public void append(String a){
        s+=a;
    }
    public void dilute(){
        append("  dilute() ");
    }
    public void apply(){
        append("  apply()");
    }
    public void scrub(){
        append("  scrub()");
    }
    public String toString(){
        return s;
    }

    public static void main(String[] args) {
        Cleanser x=new Cleanser();
        x.dilute();
        x.apply();
        x.scrub();
        System.out.println(x);

    }
}

In the example above, subclasses are derived from base classes, and subclasses can automatically obtain these public methods, although the explicit definitions of these methods in subclasses cannot be seen. Therefore, inheritance can be regarded as reuse of classes.

Exporting a class is like a new class with the same interface as the base class, and there may be additional methods and domains. But inheritance is not just about copying the interface of the base class. When an object of an export class is created, it contains subobjects of a base class. This subobject is the same as the object you create directly from the base class. The difference is that the latter creates objects directly from the base class, while the former is wrapped inside the exported class object.

The base class constructor is called in the constructor to initialize the base class subobjects.
Java automatically inserts calls to the base class constructor in the constructor of the exported class:

public class Cartoon extends Drawing {
    public Cartoon(){
        System.out.println("Cartoon Constructor");
    }

    public static void main(String[] args) {
        Cartoon cartoon=new Cartoon();
    }
}
class Drawing extends Art{
    Drawing(){
        System.out.println("Drawing Constructor");
    }
}
class Art{
    Art(){
        System.out.println("Art Constructor");
    }
}
//Result:
Art Constructor
Drawing Constructor
Cartoon Constructor

The build process is diffused outward from the base class, just as there is an implicit super() method in the constructor of the derived class. The base class is initialized before the export class constructor can access it. Even if the export class uses the default parametric constructor, the constructor will call the constructor of the base class.

If you want to call a base class constructor with parameters, you must explicitly write the statement calling the base class constructor with the keyword super, with the appropriate parameter list:
The super() method must be placed in the first sentence of the constructor.

public class Cartoon extends Drawing {

    public Cartoon(){
        super("test");
        System.out.println("Cartoon Constructor");
    }

    public static void main(String[] args) {
        Cartoon cartoon=new Cartoon();
    }
}
class Drawing extends Art{
    Drawing(String str){
        super(str);
        System.out.println("Drawing Constructor");
    }
}
class Art{
    Art(String str){
        System.out.println("str = [" + str + "]");
        System.out.println("Art Constructor");
    }
}
//Result:
str = [test]
Art Constructor
Drawing Constructor
Cartoon Constructor

Note: Constructors of base classes should be consistent with constructors of derived classes. That is, when implicit or explicit super() methods are used in the constructors of derived classes, there must be constructors with or without parameters in base classes; when explicit super(Object o) is used in the constructors of derived classes, there must be parameters in base classes. Maker.

III. Agency
Java does not provide direct support for proxies. A member object can be placed in a new class to be constructed, but by choosing a subset of methods that only provide in that member object, rather than exposing all methods of that member object in the new class.

public class SpaceShipDelegation {
    private String name;
    private SpaceshipControl sc=new SpaceshipControl();
    public SpaceShipDelegation(String name){
        this.name=name;
    }
    //Provide only a subset of methods in member object sc, not all methods
    public void up(int velocity){
        sc.up(velocity);
    }
    public void down(int velocity){
        sc.down(velocity);
    }
    public void forward(int velocity){
        sc.forward(velocity);
    }
}

class SpaceshipControl{
    void up(int velocity){};
    void down(int velocity){};
    void forward(int velocity){}
    void back(int velocity){}
}

In java, the habit is simply to forget rather than destroy objects, and let the garbage collector release its memory when necessary. When it is not known when the garbage collector will be called, or whether it will be called. Necessary cleanup activities need to be carried out during its life cycle.

public class CADSystem extends Shape{
    Circle c;
    CADSystem(int i){
        super(i);
        c=new Circle(i);
        System.out.println("CADSystem Constructor");
    }
    void dispose(){
        //Contrary to the order of constructors
        System.out.println("CADSystem dispose");
        c.dispose();
        super.dispose();
    }

    public static void main(String[] args) {
        CADSystem cad=new CADSystem(43);
        try{
            //do something
        }finally{
            cad.dispose();
        }
    }
}

class Circle extends Shape{
    Circle(int i){
        super(i);
        System.out.println("Circle Constructor");
    }
    void dispose(){
        //The cleanup order is contrary to the constructor
        System.out.println("Circle dispose");
        super.dispose();
    }
}

class Shape{
    Shape(int i){
        System.out.println("Shape Constructor");
    }
    //Custom Cleaning Method
    void dispose(){
        System.out.println("Shape dispose");
    }
}
//Result:
//Base Shape Constructor Method
Shape Constructor
//Constructor Method in Circle-like
Shape Constructor
Circle Constructor
//Constructor Method in CADSyatem-like
CADSystem Constructor
//Clearance Method in Class CADSystem
CADSystem dispose
//Clearance Method in Circle-like
Circle dispose
Shape dispose
//Clearance methods in base class Shape
Shape dispose

Put the call to the cleanup method in the final clause to indicate that the method must be called.
In order to prevent a child object from relying on another child object, the cleanup sequence is performed: first, all specific cleanup actions of the class are performed in the reverse order of generation; then, the cleanup method of the base class is invoked.

The garbage collector may never be invoked, and even if it is invoked, it may be able to recycle objects in any order it wants. The best way is not to rely on the garbage collector to do anything except memory. If you need to clean up, it's best to write your own cleaning method, but don't use finalize() method.

Name masking
If the base class has a method name that has been overloaded many times, redefining the method name in the exported class will not shield any version of it in the base class. Whether the method is defined in this layer or in its base class, the overload mechanism can work properly:

public class Hide extends Home{
    void doh(long l){
        System.out.println("doh(long)");
    }

    public static void main(String[] args) {
        Hide hide=new Hide();
        //All overloading methods are available
        hide.doh(999l);
        hide.doh('w');
        hide.doh(1);
    }
}
class Home{
    void doh(char c){
        System.out.println("doh(char)");
    }
    void doh(int i){
        System.out.println("doh(int)");
    }
}
//Result:
doh(long)
doh(char)
doh(int)

The new @Override annotation, which is not a keyword, can be used as a keyword. When you want to override a method, you can choose to add this annotation, but when you overload the method carelessly instead of overwriting it, you will report an error:

public class Hide extends Home{
    void doh(long l){
        System.out.println("doh(long)");
    }
    @Override
    void doh(char c){
        System.out.println("override the Method");
    }

    public static void main(String[] args) {
        Hide hide=new Hide();
        hide.doh(999l);
        hide.doh('w');
        hide.doh(1);
    }
}
//Result:
doh(long)
override the Method
doh(int)

Choice between Combination and Inheritance
Composition and inheritance allow subobjects to be placed in new classes. Composition does this explicitly, while inheritance does it implicitly.

Composition techniques are often used to use the functionality of existing classes in new classes rather than their interfaces. In other words, an object is embedded in the new class to realize the required functions, but the user of the new class only sees the interface defined for the new class, not the interface of the embedded object.

Inheritance means that you are using a generic class and specializing it for a particular need.

Is-a (is a) relationship is expressed by inheritance;
Has-a (with one) relationship is expressed in combination.

A clear way to judge whether to use combination or inheritance is whether to make an upward transition from a new class to a base class. Inheritance is necessary if we have to move up.

protected keyword
For ordinary class users, this is private; however, it is accessible to any derived class inherited from this class or to other classes in the same package.

Upward transformation
Traditional inheritance graphs are drawn as follows: place the root at the top of the page, and then gradually downward:

From the derived class to the base class, it moves upward on the inheritance graph, once called upward transformation.
Upward transition is a transition from a more specific type to a generic type, so it is safe, that is to say, the derived class is a superset of the base class. It may contain more methods than base classes, but it must have at least the methods contained in base classes. The relationship between them can be summarized as "new class is a type of existing class".

final keyword
1. final data
Informing the compiler of a piece of data is constant.
A compile-time constant that never changes can be substituted by the compiler into a formula that may be used, that is, to execute the formula at compile-time to reduce the burden of runtime.
In java, such a constant must be a basic data type and represented by the keyword final. When defining this constant, it must be assigned a value. But you can't assume that the value of a data is known at compile time because it is final. It's okay to know its value at runtime.

A fianl domain, even if static, occupies only an immutable storage space. Name it in capital letters and underline the words.
For final-only domains, hump nomenclature is used.

Applying final to object references keeps references constant. Once a reference is initialized to point to an object, it cannot be changed to point to another object. But the object itself can be modified. Arrays are also objects, and they apply equally.

public class FinalData {
    private int valueOne=9;
    //Both final and static final are basic types of final with compile-time values and can be used as compile-time constants without major differences.
    private final int valueTwo=99;
    private static final int VALUE_THREE=999;
    //Know the value at run time, not at compile time
    private static final int VALUE_Four= new Random(23).nextInt(10);
    private final int valueFive= new Random(53).nextInt(15);

    //final keeps references constant
    private Value obj=new Value(11);
    private final Value obj2=new Value(22);
    private static final Value OBJ_3=new Value(33);
    FinalData(){

    }
    public static void main(String[] args) {
        FinalData fd=new FinalData();
        fd.valueOne++;
        //final basic type values are immutable
        /**
        fd.valueTwo++;
        fd.VALUE_THREE++;
        fd.VALUE_Four++;
        fd.valueFive++;
         */
        //final is a non-basic type with constant references but variable object values.
        fd.obj=new Value(666);
        //References cannot be changed
        /**
        fd.obj2=new Value(7777);
        fd.OBJ_3=new Value(8888);
        */
        //Object values can be changed
        fd.OBJ_3.setI(787878);
        int obj3Value=fd.OBJ_3.getI();
        System.out.println("Value Type value="+obj3Value);

    }
}
class Value{
    int i;
    public Value(int i){
        this.i=i;
    }
    public void setI(int j){
        i=j;
    }
    public int getI(){
        return i;
    }
}
//Result:
Value Type value=787878

Blank final
java allows the generation of blank final, which refers to a field declared final but not given an initial value. In any case, the compiler ensures that the blank final must be initialized before it can be used. Therefore, final must be assigned at the domain definition or in each constructor.

public class BlankFinal {
    private final int i=0;
    private final int j;
    private final Poppet p;
    public BlankFinal(){
        j=1;
        p=new Poppet();
    }
}

class Poppet{
    Poppet(){}
}

final parameter
java allows parameters to be specified as final declaratively in the parameter list. This means that the object to which the parameter reference refers cannot be changed in the method.

public class FinalAgs {
    void with(final Game g){
        //Cannot change the object to which the parameter refers
        //g=new Game();
    }
    void without(Game g){
        g=new Game();
        g.spin();
    }
    //Readable parameters, but unable to modify parameters
    int find(final int i){
        //Unable to modify
       // i++;
        //Readable parameters
        return i+1;
    }
}
class Game{
    public  void spin(){}
}

final method
You want to ensure that method behavior remains unchanged in inheritance and is not overwritten. Lock the method in case any inheritance class modifies its meaning.

final and private keywords
All private methods in the class are implicitly specified as final. Because the private method is not available, it cannot be overwritten.

final class
When a class is defined as final as a whole, it is not intended to inherit that class, and other classes are not allowed to inherit. You don't want it to have subclasses.
The final class's domain can be customized to be final. The same rules apply to final domains, regardless of whether the class is defined as final or not.

Initial Loading

Posted by xisle on Mon, 15 Jul 2019 16:25:05 -0700