0%

Java杂记

本文主要包括:

  • Java范型

Java范型

什么是范型?

泛型(generic)是指参数化类型的能力。可以定义带泛型类型的类或方法,随后编译器会用具体的类型来代替它。

为什么要用范型?

Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

  • 类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
  • 消除强制类型转换。泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
  • 潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。

注意:
Point,这个T表示派生自Object类的任何类,但是使用哪个字母是没有特定意义的!只是为了提高可读性!!!!

遇到一个静态方法,使用如下范型:

static <T> WatermarkStrategy<T> forBoundedOutOfOrderness(Duration maxOutOfOrderness) {
        return (ctx) -> new BoundedOutOfOrdernessWatermarks<>(maxOutOfOrderness);
    }

这个方法的返回值是WatermarkStrategy<T>,前面那个T,不知道是什么意思,在用的时候,使用如下方法:

stream.assignTimestampsAndWatermarks(WatermarkStrategy
                .<Tuple2<String, Long>>forBoundedOutOfOrderness(Duration.ofSeconds(3))
                .withIdleness(Duration.ofSeconds(5))
                .withTimestampAssigner((event,timestamp) -> event.f1))
                .keyBy(x -> x.f0)
                .window(TumblingEventTimeWindows.of(Time.seconds(5)))
                .apply(new WindowFunctionTest())
                .print();

WatermarkStrategy.<Tuple2<String, Long>>:因为这个方法是静态方法,所以可以直接类名.方法,后面的.<Tuple2<String, Long>>就是方法里前面那个

Java的多态

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
多态体现为父类引用变量可以指向子类对象。
前提条件:必须有子父类关系。

注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。

  • 多态的定义与使用格式
    定义格式:父类类型 变量名=new 子类类型();

  • 多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。

  • 多态成员变量:编译运行看左边

  • 多态成员方法:编译看左边,运行看右边

    多态的转型

    多态的转型分为向上转型和向下转型两种
    向上转型:多态本身就是向上转型过的过程
    使用格式:父类类型 变量名=new 子类类型();
    适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
适用场景:当要使用子类特有功能时。

public class Main {
    public static void main(String[] args) {
        // 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税:
        Income[] incomes = new Income[] {
            new Income(3000),
            new Salary(7500),
            new StateCouncilSpecialAllowance(15000)
        };
        System.out.println(totalTax(incomes));
    }

    public static double totalTax(Income... incomes) {
        double total = 0;
        for (Income income: incomes) {
            total = total + income.getTax();
        }
        return total;
    }
}

class Income {
    protected double income;

    public Income(double income) {
        this.income = income;
    }

    public double getTax() {
        return income * 0.1; // 税率10%
    }
}

class Salary extends Income {
    public Salary(double income) {
        super(income);
    }

    @Override
    public double getTax() {
        if (income <= 5000) {
            return 0;
        }
        return (income - 5000) * 0.2;
    }
}

class StateCouncilSpecialAllowance extends Income {
    public StateCouncilSpecialAllowance(double income) {
        super(income);
    }

    @Override
    public double getTax() {
        return 0;
    }
}

样例2:

public class Test {
    static void testDuo(Test1 t){
        System.out.println("aa");
    }
    public static void main(String[] args) {
        Test2 t1 = new Test2();
        t1.test("bb");
        testDuo(t1);
    }
    public interface Test1{

    }
   public static class Test2 implements Test1{
        public void test(String str){
            System.out.println(str);
        }
    }
}

这样做的好处是:

  1. 消除类型之间的耦合关系
  2. 可替换性
  3. 可扩充性
  4. 接口性
  5. 灵活性
  6. 简化性

抽象类与接口

抽象类

在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为抽象方法,抽象方法只有方法的声明,没有方法体。抽象类的特点:

  1. 抽象类不能被实例化只能被继承;
    1.1 抽象类不能被实例化,所以抽象类必须被继承,才能被使用
    1.2 抽象类可以和类一样,实现接口,但抽象类不需要实现接口下的所有方法
    1.3 抽象类被子类继承以后,就会强迫子类充血抽象类中定义的抽象方法,除非子类也是抽象类
  2. 包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法
  3. 抽象类中的抽象方法的修饰符只能是public或者protected,默认为default
  4. 抽象类可以包含属性、方法、构造方法,但是构造方法不能用于实例化,并且子类实例化以后,抽象类的构造方法一定会被调用

接口

Java中接口用interface关键字修饰,特点为:

  1. 接口可以包含变量、方法;变量被隐式的指定为 public stastic final,方法被隐式指定为public abstract
  2. 接口支持多继承,即一个接口可以extends多个接口,间接的解决了java中类的单继承问题
  3. 一个类可以实现多个接口
  4. JDK1.8中对接口增加了新的特性:
    4.1 默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法;
    4.2 静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并可以提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)。

总结:继承是一个 “是不是”的关系,而 接口 实现则是 “有没有”的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如狗是否能钻火圈,能则可以实现这个接口,不能就不实现这个接口。

Java常用的设计模式

单例模式

解决了什么痛点或有什么好处?
保证在Java应用程序中,一个类Class只有一个实例存在。 使用单例模式的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收

什么情况下使用单例模式?
第一、控制资源的使用,通过线程同步来控制资源的并发访问;
第二、控制实例产生的数量,达到节约资源的目的。
第三、作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信。
比如,数据库连接池的设计一般采用单例模式,数据库连接是一种数据库资源

策略模式

解决了什么痛点或有什么好处?

什么情况下使用策略模式?

工厂模式

mysql的jdbc
解决了什么痛点或有什么好处?

好处
将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,明确了职责。
把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则,面向接口编程,而不是面向实现编程。

缺点:
由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

什么情况下使用工厂模式?

观察者模式

kafka的发布订阅,其实就类似于这种观察者模式
解决了什么痛点或有什么好处?

什么情况下使用观察者模式?

JVM知识点总结

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
堆中存的是对象。栈中存的是基本数据类型和堆中对象的引用。
一个对象的大小是不可估计的,或者说是可以动态变化的,但是在栈中,一个对象只对应了一个4btye的引用(堆栈分离的好处:))

JVM调优