面向对象(下)

一.包装类

基本数据类型的包装类(Byte,Short,Integer,Long,Character,Float,Double,Boolean)

二.处理对象

1).”==”和equals

1.“==”如果两个数是基本类型变量且都是数值类型,只要值相等就返回true,如果是引用类型变量,只有指向同一个对象,才会返回true,==不可用于比较没有父子关系的两个对象。

2.JVM使用常量池来管理字符串,当时用new String时,JVM先是使用常量池来管理“hello”,在调用String的构造器创建一个新的String对象,新创建的对象被保存在对内存中,一共创建了两个字符串对象。

3.常量池用于专门管理编译时被确定并保存的在已编译的class文件中的一些数据,包括类,接口,方法中的常量和字符串常量。

public class StringCompareTest {
     public static void main(String[] args) {
         String s1 = "疯狂java";
         String s2 = "疯狂";
         String s3 = "java";
         //编译时就可以确定下来,直接引用常量池中的字符串
         String s4 = "疯狂" + "java";
         String s5 = "疯" + "狂" + "java";
         //编译时不能确定下来,不能引用常量池的字符串
         String s6 = s2 + s3;
         //使用new创建一个新的String对象
         //s7将引用对内存的新创建的String对象
         String s7 = new String("疯狂java");


        System.out.println(s1 == s4);
        System.out.println(s1 == s5);
        System.out.println(s1 == s6);
        System.out.println(s1 == s7);

        //普通的equals和==没有区别,但是String重写了equals方法,字符串序列相等时返回true
        System.out.println(s1.equals(s4));
     }
}

4.String 已经重写了equals方法,String的equals方法判断两个字符串的字符序列是否相同。Object默认的equals只是比较对象的地址。与“==”结果完全相同

三.类成员

1.static修饰的成员就是类成员,类成员不能访问实例成员。

2.单例类:一个类始终只创建一个实例,类的构造器使用private修饰,隐藏构造器,使用一个public方法作为该类对象的访问点,用于创建该类的对象,且该方法必须使用static修饰(调用该方法时还不存在对象只能是类),同时还必须缓存已经创建的对象,用一个成员变量保存曾将创建的对象用于判断是否唯一。

class Singleton{
    private static Singleton instance;
    private Singleton() {
}
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
public class SingletonTest {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }

}

四.final修饰符

用于表示修饰的类,方法,变量不可改变,final修饰变量表示以一旦获得初始值就不可改变。

1).1.final修饰的成员变量必须由程序员显示指定初始值。final修饰的类变量必须在初始化块中或声明该变量时指定初始值。修饰的实力变量必须在非静态初始化块,生命该变量,或者构造器中指定初始值。

2.final修饰的成员变量必须又程序员显示初始化。

public class FinalVariableTest {
    //final修饰的类变量只能在静态初始化块或者声明时指定初始值
    //final修饰的实例变量必须在非静态初始化块,声明该实例变量或者构造器中指定初始值。
    final int a = 6;
    final String str ;
    final int c ;
    final static double d;
    {
        str = "hello";

    }
    static {
        d = 1.22;
    }
    public FinalVariableTest() {
        c = 5;
    }
    //普通方法不能对final修饰的成员变量指定初始值。
    public void test() {
        //d = 1.22;

    }
    public static void main(String[] args) {
        FinalVariableTest ft = new FinalVariableTest();
        System.out.println(ft.a);
    }

}

2).final局部变量

使用final局部变量既可以在定义时指定默认初始值,也可以不指定默认值。

final修饰的局部变量在定义时没有指定的默认值,则可以在后面的final变量赋初始值,当只能一次,不能重复赋值。

public class FinalLocalVariableTest {
    public void test(final int a) {
        //不能对final修饰的形参赋值

    }
    public static void main(String[] args) {
        //定义final局部变量指定默认值,str变量无法重新赋值
        final String str = "hello";

        //定义局部变量时没有指定默认值,则d可被赋值一次
        final double d;
        d = 5.6;
    }

}

3).final修饰的基本类型变量和引用类型变量的区别。

基本类型变量不能被改变,尔引用类型变量只是所引用的地址不会改变,对象完全可以改变。不能对引用类型变量重新赋值,但可以改变所引用对象的内容。

class Human{
    private int age;
    public Human() {

    }
    public Human(int age) {
        this.age = age;
    }
    public void  setAge(int age) {
        this.age = age;

    }
    public int getAge() {
        return this.age;
    }
}


public class FinalReference {
    public static void main (String[] args) {
        //final修饰的数组变量,iArr是一个引用变量
        final int[] iArr = {1,2,3,5,4,6,5,8,20};
        System.out.println(Arrays.toString(iArr));
        Arrays.sort(iArr);
        System.out.println(Arrays.toString(iArr));
        iArr[2] = -8;
        //对iArr重新赋值非法
        //iArr = null;


        //final修饰的human是一个引用变量
        final Human human = new Human(25);
        //改变human的值合法
        human.setAge(28);
        System.out.println(human.getAge());
        //下面语句对human重新赋值,非法
        //human = null;

    }

}

4).可执行“宏替换”的final变量

final修饰,在定义时指定初始值,在编译时就确定下来:相当于一个直接量,编译器会把所有用到该变量的地方直接替换成该变量的值。而对于普通变量,编译时无法确定其值,也就无法指向字符串池中的缓存的字符串。

public class StringJoinTest {
    public static void main(String[] args) {
        String s1 = "疯狂java";
        String s2 = "疯狂" + "java";
        System.out.println(s1 == s2);
        String str1 = "疯狂";
        String str2 = "java";
        String s3 = str1 + str2;
        System.out.println(s1 == s3);
        //final 修饰的宏替换,就是声明该变量时指定初始值,而且初始值在编译时就可以确定下来
        final String str3 = "疯狂";
        final String str4 = "java";
        String s4 = str3 + str4;
        System.out.println(s4 == s1);
    }

}

5).final 修饰的方法

1.final修饰的方法不可被重写。即使使用final修饰的private访问权限的方法,依然可以在子类中定义一个相同方法名,参数列表,相同返回值的方法,只是定义一个新方法,不是重写。

2.final修饰的方法不可被重写但完全可以被重载。

6).final类

final修饰的类不可以有子类

7).不可变类

1.不可变类:创建盖该类的实例后,该类的实例变量是不可改变的。java的8个包装类和java.lang.String都是不可变类。

2.使用private和final修饰符来修饰该类的成员变量,提供带参数的构造器用于传入参数来初始化成员变量,仅为该类的成员变量提供getter方法。

3.设计一个不可变类要注意其引用类型的成员变量,如果引用的对象是一个可变类,就必须保证其成员变量所引用的对象不会被修改。

public class Address {
    //不可变类;创建实例后实例变量不可改变。
    //使用final和privat修的好i成员变量
    //提供带参数的构造器传入参数初始化
    //仅为该类的成员变量提供getter方法
   private final String detail;
   private final String postCode;

   public Address() {
       this.detail = "";
       this.postCode = "";
   }
   public Address(String detail,String postCode) {
       this.detail = detail;
       this.postCode = postCode;
   }
   public String getDetail() {
       return this.detail;
   }
   public String getPostCode() {
       return this.postCode;
   }
}

五.抽象类

1).抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义为抽象类,抽象类可以没有抽象方法

1.抽象方法不能有方法体。

2.抽象方法不能被实例化,即使不包含抽象方法。

3.抽象类的构造器不能创建实例,主要用于被子类调用。

4.含有抽象方法的类(定义一个抽象方法,继承抽象类没有直线父类包含的抽象方法,没有实现接口的抽象方法)之只能被定义为抽象类。

5.public static void test(); 是一个抽象方法,public void test(){}是一个普通方法,方法体为空。

final 和 abstract不能同时使用(继承),static 和 abstract 不能同时修饰某个方法(属于类本身),static和final可以同时修饰内部类。abstract不能和private同时使用(重写)

public abstract class SpeedMeter {
    private double turnRate;
    public SpeedMeter() {}
    //抽象方法
    public abstract double getRadius();
    public void setTurnRate(double turnRate) {
        this.turnRate = turnRate;
    }
    public double getSpeed() {
        return java.lang.Math.PI * 2 * getRadius() * turnRate;
    }
}
public class CarSpeedMeter extends SpeedMeter {
    //实现抽象方法
    public double getRadius() {
        return 0.28;
    }
    public static void main(String[] args) {
        CarSpeedMeter csm = new CarSpeedMeter();
        csm.setTurnRate(15);
        System.out.println(csm.getSpeed());
    }

}

六 .接口

接口里允许定义默认方法,其余只能是抽象方法

1)接口中的定义

1.接口使用interface关键字,修饰符如果是public或者省略(默认包default)

2.一个接口可以有多个直接的父接口,接口只能继承接口不能继承类

3.接口不能包含构造器和初始块,包含的成员变量(静态变量),方法(抽象实例方法,类方法,默认方法),内部类(包括内部接口,枚举)

4.接口是公共行为规范,接口的所有成员都是public(可以省略)

5.对于静态常量,系统自动添加final static 修饰。

6.接口里定义的方法默认使用public abstract修饰,不能有方法体,但类方法,默认方法必须有方法体。

7.在接口中定义默认方法需要使用default修饰,类方法需要static修饰。默认方法需要接口的实现类的实例调用。接口里的成员变量默认使用public static final 修饰的,因此即使另一个类处于不同的包下,也可以通过接口来访问接口里成员变量。

public interface Output {
    //接口里定义 的成员变量只能是常量,默认public final static 
    int MAX_CACHE_LINE = 50;
    //接口里的定义的普通方法是public的抽象方法
    void out();
    void getData(String msg);
    //接口里定义的默认方法需要使用default修饰,需要用接口实现类的实例来调用这些方法
    default void print(String... msgs) {
        for(String msg : msgs) {
            System.out.println(msg);
        }
    }
    default void test() {
        System.out.print("tst");
    }
    //接口的类方法可以直接使用接口调用
    static String staticTest() {
          return "abc";     
    }

}

2).接口的继承

1.接口完全支持多继承(extends),一个借口可以有多个直接父接口,子接口扩展某个父接口,将会获得父接口所有抽象方法和常量。

3).使用接口

1.接口不能用于创建实例,但是接口可以用于声明引用类型的变量,当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象,除此之外,接口的主要用途就是被实现类实现。(定义变量,也可用于强制类型转换,调用接口中定义的常量,被其他类实现)继承使用extends,实现使用implement,implement必须放在extends之后。

2.一个类实现接口时必须完全实现这些接口所定义的全部抽象方法,并将获得接口中定义的常量,方法等。

3.接口的实现类的方法只能是public修饰

4).接口和抽象类

1.接口和抽象类都不能被实例化。接口只能包含抽象方法,静态方法和默认方法,抽象类可以包含普通方法。

2.接口只能定义静态常量,不能定义普通成员变量,抽象类可以

3.接口不包含构造器,抽象类包含构造器用于子类调用完成属于抽象类的初始化操作。

4.接口不能包含初始化块,抽象类可以。

6).面向接口的编程

简单工厂模式


public class Computer {
    private Output out;
    public Computer (Output out) {
        this.out = out;
    }

    public void keyIn(String msg) {
        out.getData(msg);
    }
    public void print() {
        out.out();
    }

}

public class OutputFactory {
    public Output getOutput() {
        //
        //return new Printer();
    }

    public static void main(String[] args) {
        OutputFactory of = new OutputFactory();
        Computer c = new Computer(of.getOutput());
    }

}

命令模式


public interface Command {
    void process(int[] target);

}
public class ProcessArray {
    public void process(int[] target, Command cmd) {
        cmd.process(target);

    }

}
public class CommandTest {
      public static void main(String[] args) {
          ProcessArray pa = new ProcessArray();
          int target[] = {3,4,5,6,5};
          pa.process(target, new PrintCommand());
          System.out.println("--------------");
          pa.process(target, new AddCommand());
      }
}
public class PrintCommand implements Command {

    public void process(int[] target) {
        // TODO Auto-generated method stub
        for(int tmp : target) {
            System.out.println(tmp);
        }

    }

}
public class AddCommand implements Command {

    @Override
    public void process(int[] target) {
        // TODO Auto-generated method stub
        int sum = 0;
        for(int tmp : target) {
            sum = sum + tmp;

        }
       System.out.println(sum);
    }

}

七.内部类

内部类成员可以访问外部类的私有数据,但外部类不能访问内部类的细节。匿名内部类适合用于创建那些仅需要一次使用的类。内部类可以使用修饰符(public,private,protected,static),非静态成员不能拥有静态成员。

1).非静态内部类(没有使用static修饰)

1.如果外部类成员变量内部类成员变量内部类方法的局部变量同名,则可使用this,外部类名.this作为区分限定。

2.非静态内部类的成员变量可以访问外部类的private,反过来就不行。如果外部类需要访问非静态内部类的成员,则必须显示创建非静态内部类对象来调用实例成员。

public class Outer {
    private int outProp = 9;
    class Inner{
        private int inProp = 5;
        public  void  acessOuterProp() {
            //非静态内部类可以直接访问外部类的private成员变量
            System.out.println(outProp);
        }
    }
    public void accessInnerProp() {
        //外部类不能访问非静态内部类的实例变量,只能显式创建内部类对象
        System.out.println(new Inner().inProp);
    }
    public static void main(String[] args) {
        //执行下面代码,只创建外部类对象的,还未创建内部类对象
        Outer outer  = new Outer();
        outer.accessInnerProp();
    }

}

2).静态内部类(static修饰)

1.static修饰的内部类则这个内部类就属于外部类本身,不属于外部类的对象

2.静态内部类不能访问外部类的实例成员,只能访问外部类的类成员,即使静态内部类的实例方法也不行。(静态内部类的对象存在时并不存在一个被它寄生的外部类对象,静态内部类对象支持有外部类的类引用,没有持有外部类对象的引用)

3.外部类不能直接访问静态内部类的类成员,当可以直接使用静态内部类的类名作为调用者。

4.接口里定义的内部类默认使用public static修饰,也就是说接口内部类只能是静态内部类。

public class AccessStaticInner {
    static class StaticInnerClass{
        private static int prop1 = 5;
        private int prop2 = 9;


    }
    public void accessInnerProp() {
        //通过类名访问静态内部类的类成员
        System.out.println(StaticInnerClass.prop1);
        //通过实例访问静态内部类的实例成员
        System.out.println(new StaticInnerClass().prop2);
    }

}

3).使用内部类

1.不能在外部类的静态成员使用非静态类,private 修饰的内部类只能被外部类使用。由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前必须创建外部类对象

2.在外部类以外的地方创建非静态内部类的子类:非静态内部类的构造器必须由外部类的对象调用。调用非静态内部类的构造器时,必须存在一个外部类对象。

3.在外部类以外使用静态内部类,只需使用外部类即可调用构造器。(内部类的类名不再是简单的类名实际还包括外部类,所以不能被重写)

class Out{
    //定义一个内部类
    class In{
        public In(String msg) {
            System.out.println(msg);
        }
    }
}
public class CreateInnerInstance {
    public static void main(String[] args) {
        //非静态内部类的实例的构造器必须通过外部类的实例调用
        Out.In in = new Out().new In("信息");
        /*
         * 上面代码如下
         * 使用OutterClass.InnerClass的形式定义一个内部类
         * Out.In in;
         * 创建一个外部类的实例,非静态内部类实例将寄生在该实例中
         * Out out = new Out();
         * 通过外部类实例和new来调用内部类构造器创建非静态内部类的实例
         * in = out.new In("信息");
         */

    }


}
class StaticOut{
    static class StaticIn{
        public StaticIn() {
            System.out.println("静态内部类的构造器");
        }
    }
}
public class CreateInnerInstance2 {
    public static void main(String[] args) {
        //静态内部类的实例直接通过外部类调用
        StaticOut.StaticIn  in = new StaticOut.StaticIn();
        /*
         * 定义内部类变量
         * StaticOut.Static.in in;
         * in = new StaticOut.Static.in();
         */
    }

}

4).局部内部类

如果把一个内部类放在一个方法里定义,那就是局部内部类,仅在方法内有效。

5).匿名内部类(适用于只需要一次使用的类)

1.匿名内部类必须继承一个父类或者实现一个接口,最多只继承一个父类或者一个接口。

2.匿名内部类不能使抽象类,因为创建匿名内部类时会立即创建匿名内部类的对象。匿名内部类不能定义构造器,没有类名,可以有初始化块完成构造器需要完成的事。

3.当使用实现接口时匿名内部类只有一个隐式的无参数构造器。使用继承父类来创建时,匿名内部类将拥有和父类构造器相同形参列表的构造器。

4.当创建匿名内部类时必须实现接口或抽象父类的所有抽象方法,也可以重写父类的普通方法。

5.被匿名内部类访问的局部变量可以用final修饰,也可以不用,但要按照final修饰的方式来用(只能赋值一次)

class StaticOut{
    static class StaticIn{
        public StaticIn() {
            System.out.println("静态内部类的构造器");
        }
    }
}
public class CreateInnerInstance2 {
    public static void main(String[] args) {
        //静态内部类的实例直接通过外部类调用
        StaticOut.StaticIn  in = new StaticOut.StaticIn();
        /*
         * 定义内部类变量
         * StaticOut.Static.in in;
         * in = new StaticOut.Static.in();
         */
    }

}
abstract class Device{
    private String name;
    public abstract double getPrice();
    public Device() {}
    public Device(String name) {
        this.name = name;

    }

    public void setName(String name) {
        this.name  = name;

    }
    public String getName() {
        return this.name;
    }
}

public class AnonymousInner {
    public void test(Device d) {
        System.out.println(d.getName()+d.getPrice());
    }

    public static void main(String[] args) {
        //调用有参数的构造器
        AnonymousInner  ai = new AnonymousInner();
        ai.test(new Device("java") {
            public double getPrice() {
                return 0.22;
            }

        });
        //调用无参数的构造器
        Device d  = new Device() {
            {
                System.out.println("匿名内部类的初始化块");
            }
            public double getPrice() {
                return 0.58;
            }
            public String getName() {
                return "java";
            }
        };

        ai.test(d);
    }

}

八.Lambda表达式

用来创建只有一个抽象方法的接口(称为函数式接口)的实例

//匿名内部类
pa.process(target , new Command(){
   public void process(int[] target){
       ...
   }
});
//Lambda表达式
pa.process(target , (int target)-> {
    ...
});

1.Lambda表达式只要给出重写的方法括号以及括号里的形参即可,相当于一个匿名方法。

2.形参列表允许省略形参类型,形参列表只有一个参数可以省略圆括号,Lambda表达式如果只包含一条语句允许省略花括号,Lambda表达式需要返回值,它的代码仅有一条省略的return语句,Lambda会自动返回这条语句的值

interface Eatable{
    public void taste();
}
interface Flyable {
    public void fly(String str);

}
interface Addable{
    public int add(int a,int b);
}
public class LambdaQs {
    public void eat(Eatable e) {
        System.out.println(e);
        e.taste();
    }
    public void fly(Flyable e) {
        System.out.println(e);
        e.fly("天空");

    }
    public void test(Addable e) {
        System.out.println(e);
        e.add(1, 2);

    }
    public static void main(String[] args) {
        LambdaQs lq = new LambdaQs();
        //代码块只有条语句,可以省略花括号
        lq.eat(()->System.out.println("eat"));
        //形参列表只有一个形参可以省略圆括号
        lq.fly(weather->System.out.println(weather));
        //只有一条语句,可以省略圆括号,即使需要返回值也可以省略return关键字
        lq.test(( a, b)-> a+b);
    }

}

2)Lambda表达式与函数式接口

1.表达式的类型也被称为目标类型,必须是函数式接口(可以包含默认方法,类方法,但只能声明一个抽象方法)

2.Lambda表达式的结果就是被当作对象,因此可以使用表达式进行赋值。

3.Lambda表达式的目标类型必须是明确的函数式接口,并且只能为函数式接口创建对象。或使用函数式接口对表达式进行强制类型转换。

3).方法引用和构造器引用

如果代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用

1.引用类方法

interface Converter{
    Integer convert(String from);
}
//使用Lambda表达式
Converter converter = from->Integer.valueOf(from);
//引用类方法,被实现方法的参数全部传递给该类方法作为参数
Converter converter = Integer::valueOf;
Integer val = converter.convert("99");

2.引用特定对象的实例方法

函数式接口被实现的方法全部参数传给该方法作为参数

3.引用某类对象的实例方法

函数式接口被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数。

4.引用构造器

函数式接口的被实现的方法的全部参数传给该构造器作为参数。

5).Lambda表达式与匿名内部类的区别

1.匿名内部类可以为任意接口创建实例,表达式只能为函数式接口,匿名内部类可以为抽象类甚至普通类创建实例,匿名内部类实现的抽象方法允许调用接口中的默认方法,Lambda表达式不允许。

九.枚举类

1).枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是继承Object类。非抽象的枚举类默认使用final修饰,枚举类的构造器也只能使用private访问控制符,枚举类的所有实例都必须在每句类的第一行显示列出,否则将不能产生实例,系统会自动添加public static final ,枚举类提供了一个values()方法,用于遍历所有枚举值。

public enum SeasonEnum{
    SRPRING,SUMMER,FALL,WINTER;
}

枚举类的成员变量都应使用private final修饰变成不可变类。然后必须在构造器里为这些成员变量指定初始值因此应该为枚举类显示定义带参数的构造器。

public enum Gender{
    MALE("男"),FEMALE("女");
    private Gender (String name){
        this.name = name;

    }
    public String getName(){
        return this.name;
    }
}

十.垃圾回收机制

垃圾回收机制只负责回收堆内存的对象,不会回收任何物理资源(数据库连接,网络IO)

可达状态:有至少一个引用变量引用。

可恢复状态:不再有任何引用变量引用它,会调用finalize()进行资源清理。是恢复可达或进入不可达

不可达状态:永久性失去引用,系统真正收回对象所占有的资源。

猜你喜欢

转载自blog.csdn.net/m0_38089373/article/details/74937341