从0开始复习java(3)

一、面向对象

1、包装类

java5之后有自动装箱和自动拆箱功能。

包装类可以实现基本类型变量和字符串之间的转换。

字符串转基本数据类型:

  • 包装类的静态方法parseType(String s)
  • 利用包装类提供的type(String s)构造器。(如int a = Integer.int("123")

基本数据类型转字符串:

  • String.valueOf(Type t)
  • "" + t

Integer-128~127直接用的是cache数组中的内容:

static final Integer[] cache = new Integer[-(-128)+127+1];
static {
    for(int i=0; i <= cache.length; i++){
        cache[i] = new Integer(i-128);
    }
}

java7为所有的包装类提供了compare(Type a, Type b)静态方法。

java8增加了无符号算术运算。

2、toString方法

Java对象都是Object类的实例,可以直接调用该类的方法,这些方法提供了处理Java对象的通用方法。

3、equals方法

Object类中,equals==没有区别。

public boolean equals(Object obj){
    if (this==obj){
        return true;
    }
    if (obj != null && obj.getClass() == Person.Class){
        Person personObj = (Person)obj;
        if (this.getIdStr().equals(personObj.getIdStr())){
            return true;
        }
    }
    return false;
}

4、类成员

static修饰的就是类成员。包括类变量、类方法、静态初始化块。

类成员不能访问实例变量。

Java类里面有:

  • 成员变量
  • 成员函数
  • 构造器
  • 初始化块
  • 内部类(接口、枚举等)

5、单例类

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstace(){
        if(isntance==null){
            instance = new Singleton();
        }
        return instance;
    }
}

6、final修饰符

final修饰的变量不可被修改。可以修饰成员变量、局部变量、形参等。

没有定义初始值,也没有在初始化块、构造器中指定初始值,则这些变量将是系统分配的默认值0,'\u0000',false,null,其失去意义。因此Java规定:final修饰的成员变量必须由程序员显示的指定初始值。

final修饰类变量和实例变量的位置:

  • 类变量:静态初始化块或者声明的地方
  • 实例变量: 非静态初始化块或声明的地方或构造器

7、可执行“宏替换”的final变量

只要满足三个条件,即变成直接量,而非变量。

  • 使用final修饰
  • 定义是指定初始值
  • 改初始值可以在编译时确定下来

8、final方法和final类

final定义的方法不可以被重写。

Object类里面有一个final类getClass()

final类不可以有子类。java.lang.Math

9、抽象类

抽象方法是只有方法签名,没有方法实现的方法。

有抽象方法的类只能是抽象类,抽象类中可以没有抽象方法。

  • 抽象类必须使用abstract修饰符,抽象方法也要使用abstract修饰符,抽象方法不能有方法体。
  • 抽象类不能被实例化。
  • 抽象类可以有成员变量、方法、构造器、初始化块、内部类。抽象类的构造器主要用于子类调用,不能用于创建实例。
  • 含有抽象方法的类只能被定义为抽象类。

利用抽象类和抽象方法可以更好的发挥多态的优势。

staticabstract不能同时修饰某个方法。没有类抽象方法。

static 和 abstract 可以同时修饰内部类。

抽象类体现了模板模式的设计。

10、接口

接口里的所有方法都是抽象方法,Java8对接口进行了改进,允许定义默认方法,默认方法可以提供方法实现。

[修饰符] interface Name extends Name1, Name2{
    0个到多个常数定义;
    0个到多个抽象方法;
    0个到多个内部类、接口、枚举;
    0个到多个默认方法或类方法定义;
}

修饰符可以是public或者省略
接口只能继承接口,不能继承类

接口里面可以包含静态常量、抽象实例方法、类方法或默认方法、内部类。

只能使用public访问权限,可以省略。

int MAX_SIZE = 50;
public static final int MAX_SIZE = 50;

普通方法使用public abstract修饰,可以省略。

类方法和默认方法必须实现。

默认方法必须使用default修饰,该方法不能使用static修饰,默认添加public修饰符。需要调用接口的实现类的实例来调用这些默认方法。

类方法必须用static修饰,不能使用default修饰,默认添加public修饰符。类方法可以通过接口访问。

一个java源文件离只能有一个public接口。

public interface Output{
    int MAX_CACHE_LINE = 50;
    void getData();
    void out();
    default void print(String... msgs){
        for (String s : msgs){
            System.out.println(s);
        }
    }
    static String staticTest(){
        return "123";
    }
}

接口支持多继承。

接口的主要用途:

  • 定义变量,也可用于进行类型转换
  • 调用接口中定义的常量
  • 被其他类实现

让类实现接口需要类定义后增加implements部分。一个类可以继承一个父类,并同时实现多个接口,implements部分必须放在extends部分之后。

11、接口和抽象类

特征:

  1. 都不能被实例化,都位于继承树的顶端,用于被其他类实现和继承。
  2. 都可以包含抽象方法,实现接口和继承抽象类的普通子类都必须实现这些抽象方法。

区别:

  1. 接口体现的是一种规范。对于接口的实现者,接口规定了实现者必须向外提供哪些服务。对于接口的调用者,接口规定了调用者可以调用哪些服务,以及如何调用这些服务。
  2. 抽象类体现的是一种模板式设计。

用法差异:

  1. 接口只能包含抽象方法,静态方法和默认方法,不能为普通方法提供方法实现;抽象类可以包含普通方法。
  2. 接口里只能定义静态变量,不能定义普通成员变量;抽象类都可以。
  3. 接口里不包含构造器;抽象类可以包含构造器
  4. 接口里不能包含初始化块;抽象类可以
  5. 一个类最多只能有一个直接父类,包括抽象类;一个类可以实现多个接口,通过实现多个接口可以弥补Java单继承的不足。

12、内部类

作用:

  1. 提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
  2. 内部类可以访问外部类,但是外部类不能访问内部类的细节。
  3. 匿名内部类适合只使用一次的类。

内部类的区别:

  1. 比外部类多三个修饰符:private,protected,static
  2. 非静态内部类不能拥有静态成员

成员内部类和局部内部类

  • 成员内部类是和成员变量、成员函数、构造器、初始化块相似的类成员,分为静态内部类和非静态内部类
  • 局部内部类和匿名内部类不是类成员

非静态内部类

静态内部类

静态内部类里面可以有静态成员,也可以有非静态成员。

接口里的内部类默认使用public static修饰,接口内部类只能是静态内部类。

使用内部类:

  1. 在外部类内部使用内部类
  2. 在外部类以外使用非静态内部类
  3. 在外部类以外使用静态内部类

使用静态内部类比使用非静态内部类简单得多,当程序需要时,应该优先考虑使用静态内部类

局部内部类:定义在方法里面的类。

实际开发中很少定义局部内部类

匿名内部类:只需使用一次,创建匿名类会立即创建这个类的实例,类定义立即消失。

语法:

new 实现接口() | 父类构造器(参数名称){
    //类体
}

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

规则:

  • 匿名内部类不能是抽象类
  • 不能定义构造器,但是可以定义初始化块

最常用的创建匿名类的方式是需要创建某个接口类型的对象。

interface Product{
    double b=5;
    double getPrice();
    String getName();
}

public class Test{
    public void test(Product p){
        System.out.println(p.getName()+p.getPrice());
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.test(new Product(){
            public String getName(){
                return "JAVA:";
            }
            public double getPrice(){
                return b;
            }
        });
    }
}

当使用接口创建内部类时,匿名内部类不能显式创建构造器,只有一个隐式的无参构造器,new接口之后的括号不能传入参数值。

如果通过继承父类创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,相似指的是拥有相同的形参列表。


abstract class Device{
    private String name;
    public String getName(){
        return "java";
    }
    public abstract double getPrice();
    public Device(){}
    public Device(String name){
        this.name = name;
    }
}
public class Test{
    public void test(Device d){
        System.out.println(d.getPrice()+d.getName());
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.test(new Device("Java"){
            public double getPrice(){
                return 5;
            }
        });
        Device d = new Device(){
            //初始化块
            {
                System.out.println("匿名内部类的初始化块");
            }
            //实现抽象方法
            public double getPrice(){
                return 5;
            }
            //重写父类方法
            public String getName(){
                return "Java2";
            }
        };
        t.test(d);
    }
}

被局部内部类、匿名内部类访问的局部变量必须使用final修饰,Java8更加智能:

如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰符。Java8将这个功能成为effectively final

interface A {
    public static final int a = 1;
    public abstract void test();
}
public class ATest{
    public static void main(String[] args){
        int age = 10;
        A a = new A(){
            public void test(){
                System.out.println(age);
            }
        };
    }
}

以上的程序中age将会用final修饰。

13、lambda表达式

lambda表达式支持将代码块作为方法参数,lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(函数式接口)的实例。

主要作用是代替匿名内部类的繁琐语法。它由三部分构成:

  • 形参列表:只有一个参数,可以省略括号
  • 箭头
  • 代码块:只有一条语句,则可以省略花括号,也可以省略return

Lambda表达式实际上可以被当成“任意类型”的对象,取决于运行环境的需要。

14、lambda表达式与函数式接口

Lambda表达式的目标类型必须是“函数式接口”,即只有一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能声明一个抽象方法。

RunnableActionListener都是函数式接口。

java8专门为函数式接口提供了@FunctionalInterface注解,该注解通常放在接口定义前面,该注解对程序功能没有任何作用,用于告诉编译器--检查该接口必须是函数式接口,否则编译器会出错。

//Runnable接口只包含一个无参的方法
Runnable r = () -> {
    for(int i=0; i<100; i++){
        System.out.println(i);
    }
};

为了保证lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式:

  • 将lambda表达式赋值给函数式接口的变量
  • 将lambda表达式作为函数式接口类型的参数传给某个方法
  • 使用函数式接口对lambda表达式进行强制类型转换
Object obj = (Runnable)()->{
    for (int i=0; i<100; i++){
        System.out.println(i);
    }
};

定义一个函数式接口

@FunctionalInterface
interface FkTest{
    void run();
}

java8在java.util.function包下预定义了大量函数式接口,典型的有如下4类:

  • XxxFunction:这类接口通常包含一个apply()抽象方法。用于对指定数据进行转换处理。
  • XxxConsumer:通常包含一个accept()抽象方法。用于对参数进行处理,但是没有返回值。
  • XxxPredicate:通常包含一个test()抽象方法。用来对参数进行某种判断,判断是否满足特定条件,用于进行筛滤数据,返回boolean值。
  • XxxSupplier:通常包含一个getAsXxx()抽象方法,不需要输入参数,按某种逻辑返回一个数据。

15、方法引用和构造器引用

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

种类 示例 说明 对应的lambda表达式
引用类方法 类名::类方法 函数式接口中被实现方法的所有参数传递给该类方法作为参数 (a,b..)->类名.类方法(a,b...)
引用特定对象的实例方法 特定对象::实例方法 函数式接口中被实现方法的所有参数传递给该方法作为参数 (a,b..)->特定对象.实例方法(a,b...)
引用某类对象的实例方法 类名::实例方法 函数式接口中被实现方法的第一个参数作为调用者,后面的所有参数传递给该方法作为参数 (a,b..)->a.实例方法(b...)
引用构造器 类名::new 函数式接口中被实现方法的所有参数传给该构造器作为参数 (a,b..)->new 类名(a,b...)

引用类方法

@FunctionalInterface
interface Converter{
    Integer convert(String from);
}
Converter c = from -> Integer.valueOf(from);
Integer val = c.convert("99");
Converter c = Integer::valueOf;

引用特定对象的实例方法

Converter c = from -> "fkit.org".indexOf(from);
Integer val = c.convert("it");
Converter c = from -> "fkit.org"::indexOf;

引用某类对象的实例方法

@FunctionalInterface
interface MyTest{
    String test(String a, int b, int c)
}
Mytest t = (a, b, c) -> a.substring(b,c);
String str = t.test("I love java", 2, 5);
Mytest t = String::substring;

引用构造器

@FunctionalInterface
interface MyTest{
    JFrame win(String title);
}
Mytest t = str -> new JFrame(str);
JFrame f = t.win("I love java");
Mytest t = JFrame::new;

16、lambda表达式与匿名内部类的联系和区别

相同:

  • 都可以访问effectively final的局部变量,以及外部类的成员变量
  • 都可以直接调用从接口中继承的默认方法

区别:

  • 匿名内部类可以为任意接口创建实例;lambda表达式只能为函数式接口创建实例。
  • 匿名内部类可以为抽象类甚至是普通类创建实例
  • 匿名内部类实现的抽象方法体允许调用接口中定义的默认方法;lambda表达式不允许。

17、枚举类

实例有限而且固定的类

java5新增了一个enum关键字(与classinterface地位相同),用以定义枚举类。

枚举类与普通类的区别:

  • 枚举类可以实现一个或者多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,因此枚举类不能显示继承其他父类(单继承)。
  • 使用enum定义、非抽象的枚举类默认使用final修饰,不能派生子类。
  • 构造器只能使用privte访问控制符。
  • 所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远不会产生实例。系统会自动添加public static final修饰符。

枚举类默认提供了values方法,可以遍历所有的枚举值。

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

这些枚举值代表了枚举类所有可能的实例。

public class EnumTest{
    public void judge(SeasonEnum s){
        switch (s){
            case SPRING:
                System.out.println("Spring");
                break;
            case SUMMER:
                System.out.println("Summer");
                break;
            case FALL:
                System.out.println("Fall");
                break;
            case WINTER:
                System.out.println("WINTER");
                break;
            default:
                System.out.println("Not exist");
        }
    }
    public static void main(String[] args) {
        for(SeasonEnum s: SeasonEnum.values()){
            System.out.println(s);
        }
        new EnumTest().judge(SeasonEnum.SPRING);
    }
}

java.lang.Enum默认提供以下方法:

  1. int compareTo(E o)

用于指定枚举对象比较顺序

  1. String name()

返回枚举实例的名称,应该尽可能使用toString()方法

  1. int ordinal()

返回枚举值在枚举类中的索引值(从0开始)。

  1. String toString()

返回枚举常量的名称

  1. public static > T valueOf(Class enumType, String name)

返回指定枚举类中指定名称的枚举值。

18、枚举类的成员变量、方法和构造器

枚举类通常应该设计成不可变的,建议将枚举类的成员变量用private final修饰。

必须在构造器里为这些成员变量指定初始值(也可以在初始化块或者定义成员变量时指定,但是不常用)。

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

MALE("男")相当于public static fianl Gender MALE = new Gender("男")

19、枚举类实现接口

public interface GenderDesc{
    public abstract void info();
}
public enum Gender implements GenderDesc{
    MALE("男"){
        public void info(){
            //sth
        }
    },
    FEMALE("女") {
        public void info(){
            //sth else
        }
    }
}
//constructor and instance method

这种情况下,创建MALEFEMALE枚举值时,并不是直接创建Gender枚举类的实例,而是相当于创建Gender的匿名子类的实例。


并不是所有的枚举类都使用了final修饰,非抽象类的枚举类才使用final修饰。


枚举类里定义抽象方法时不能使用abstract修饰枚举类(系统自动添加),但是由于枚举类需要显式创建枚举值,而不是作为父类,因此定义每个枚举值时都要为抽象方法提供实现,否则讲出现编译出错。

20、修饰符的适用范围

从0开始复习java(3)_第1张图片
修饰符适用范围

strictfp即(FP-strict),精确浮点的意思。一旦使用该关键字修饰类、接口或者方法,所修饰范围内java的编译器和运行环境会完全依照浮点规范IEEE-754来执行。


native关键字用来修饰一个方法,用该关键字修饰的方法类似于抽象方法。不同点在于native方法通常采用c实现。如果某个方法需要利用平台相关特性或者访问硬件等可以用该关键字。一旦使用则改程序失去跨平台的特性。


4个访问控制符是互斥的,最多只能出现一个。abstract和final不能同时使用;abstract和static不能同时修饰方法,可以同时修饰内部类;abstract和private不能同时修饰方法,可以同时修饰内部类;private和final同时修饰方法可以同时修饰方法,但是没有意义(private不能被继承)。

你可能感兴趣的:(从0开始复习java(3))