关于java:2021最新Java知识总结助力大厂offer

34次阅读

共计 18276 个字符,预计需要花费 46 分钟才能阅读完成。

本文是我花了三周工夫整理出来的,心愿对 Java 初学者有帮忙~

Java 概述

Java 的特点

Java 是一门面向对象的编程语言。面向对象和面向过程是一种软件开发思维。

  • 面向过程就是剖析出解决问题所须要的步骤,而后用函数按这些步骤实现,应用的时候顺次调用就能够了。面向对象是把形成问题事务分解成各个对象,别离设计这些对象,而后将他们组装成有残缺性能的零碎。面向过程只用函数实现,面向对象是用类实现各个功能模块。

    例如五子棋,面向过程的设计思路就是首先剖析问题的步骤:
    1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤 2,9、输入最初后果。
    把下面每个步骤用别离的函数来实现,问题就解决了。

  • 而面向对象的设计则是从另外的思路来解决问题。整个五子棋能够分为:
    1、黑白单方
    2、棋盘零碎,负责绘制画面
    3、规定零碎,负责断定诸如犯规、输赢等。
    黑白单方负责承受用户的输出,并告知棋盘零碎棋子布局发生变化,棋盘零碎接管到了棋子的变动的信息就负责在屏幕下面显示出这种变动,同时利用规定零碎来对棋局进行断定。

Java 具备平台独立性和移植性。

  • Java 有一句口号:Write once, run anywhere,一次编写、到处运行。这也是 Java 的魅力所在。而实现这种个性的正是 Java 虚拟机 JVM。已编译的 Java 程序能够在任何带有 JVM 的平台上运行。你能够在 windows 平台编写代码,而后拿到 linux 上运行。只有你在编写完代码后,将代码编译成.class 文件,再把 class 文件打成 Java 包,这个 jar 包就能够在不同的平台上运行了。

Java 具备稳健性。

  • Java 是一个强类型语言,它容许扩大编译时查看潜在类型不匹配问题的性能。Java 要求显式的办法申明,它不反对 C 格调的隐式申明。这些严格的要求保障编译程序能捕获调用谬误,这就导致更牢靠的程序。
  • 异样解决是 Java 中使得程序更持重的另一个特色。异样是某种相似于谬误的异样条件呈现的信号。应用 try/catch/finally 语句,程序员能够找到出错的解决代码,这就简化了出错解决和复原的工作。

JKD 和 JRE

JDK 和 JRE 是 Java 开发和运行工具,其中 JDK 蕴含了 JRE,而 JRE 是能够独立装置的。

JDK

Java Development Kit,JAVA 语言的软件工具开发包,是整个 JAVA 开发的外围,它蕴含了 JAVA 的运行(JVM+JAVA 类库)环境和 JAVA 工具。

JRE

JRE(Java Runtime Environment,Java 运行环境):蕴含 JVM 规范实现及 Java 外围类库。JRE 是 Java 运行环境,并不是一个开发环境,所以没有蕴含任何开发工具(如编译器和调试器)。

JRE 是运行基于 Java 语言编写的程序所不可短少的运行环境。也是通过它,Java 的开发者才得以将本人开发的程序公布到用户手中,让用户应用。

Java 根底语法

根本数据类型

  • byte,8bit
  • char,16bit
  • short,16bit
  • int,32bit
  • float,32bit
  • long,64bit
  • double,64bit
  • boolean,只有两个值:true、false,能够使⽤用 1 bit 来存储,然而具体⼤小没有明确规定。

包装类型

根本类型都有对应的包装类型,根本类型与其对应的包装类型之间的赋值通过主动装箱与拆箱实现。

Integer x = 1; // 装箱 调⽤ Integer.valueOf(1)
int y = x; // 拆箱 调⽤了 X.intValue()

包装类缓存

应用 Integer.valueOf(i)生成 Integer,如果 -128 <= i <= 127,则间接从 cache 取对象返回,不会创立新的对象,防止频繁创立包装类对象。

    public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

String

String 是 final 类,不可被继承。

String 拼接

字符串拼接能够应用 String 用 + 做拼接,也能够应用 StringBuilder 和 StringBuffer 实现,三种形式比照:

  • 底层都是 char 数组实现的
  • 字符串拼接性能:StringBuilder > StringBuffer > String
  • String 是字符串常量,一旦创立之后该对象是不可更改的,用 + 对 String 做拼接操作,实际上是先通过建设 StringBuilder,而后调用 append()做拼接操作,所以在大量字符串拼接的时候,会频繁创立 StringBuilder,性能较差。
  • StringBuilder 和 StringBuffer 的对象是字符串变量,对变量进行操作就是间接对该对象进行批改(批改 char[]数组),所以速度要比 String 快很多。
  • 在线程平安上,StringBuilder 是线程不平安的,而 StringBuffer 是线程平安的,StringBuffer 中很多办法带有 synchronized 关键字,能够保障线程平安。

关键字

static

static 能够用来润饰类的成员办法、类的成员变量。

动态变量

static 变量也称作动态变量,动态变量和非动态变量的区别是:动态变量被所有的对象所共享,在内存中只有一个正本,它当且仅当在类首次加载时会被初始化。而非动态变量是对象所领有的,在创建对象的时候被初始化,存在多个正本,各个对象领有的正本互不影响。

以下例子,age 为非动态变量,则 p1 打印后果是:Name:zhangsan, Age:10;若 age 应用 static 润饰,则 p1 打印后果是:Name:zhangsan, Age:12,因为 static 变量在内存只有一个正本。

public class Person {
    String name;
    int age;
    
    public String toString() {return "Name:" + name + ", Age:" + age;}
    
    public static void main(String[] args) {Person p1 = new Person();
        p1.name = "zhangsan";
        p1.age = 10;
        Person p2 = new Person();
        p2.name = "lisi";
        p2.age = 12;
        System.out.println(p1);
        System.out.println(p2);
    }
    /**Output
     * Name:zhangsan, Age:10
     * Name:lisi, Age:12
     *///~
}

静态方法

static 办法个别称作静态方法。静态方法不依赖于任何对象就能够进行拜访,通过类名即可调用静态方法。

public class Utils {public static void print(String s) {System.out.println("hello world:" + s);
    }

    public static void main(String[] args) {Utils.print("程序员大彬");
    }
}

动态代码块

动态代码块只会在类加载的时候执行一次。以下例子,startDate 和 endDate 在类加载的时候进行赋值。

class Person  {
    private Date birthDate;
    private static Date startDate, endDate;
    static{startDate = Date.valueOf("2008");
        endDate = Date.valueOf("2021");
    }

    public Person(Date birthDate) {this.birthDate = birthDate;}
}

动态外部类

在静态方法里,应用⾮动态外部类依赖于外部类的实例,也就是说须要先创立外部类实例,能力用这个实例去创立非动态外部类。⽽动态外部类不须要。

public class OuterClass {class InnerClass {}
    static class StaticInnerClass { }
    public static void main(String[] args) {
        // 在静态方法里,不能间接应用 OuterClass.this 去创立 InnerClass 的实例
        // 须要先创立 OuterClass 的实例 o,而后通过 o 创立 InnerClass 的实例
        // InnerClass innerClass = new InnerClass();
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();

        outerClass.test();}
    
    public void nonStaticMethod() {InnerClass innerClass = new InnerClass();
        System.out.println("nonStaticMethod...");
    }
}

final

  1. 根本数据 类型用 final 润饰,则不能批改,是常量;对象援用 用 final 润饰,则援用只能指向该对象,不能指向别的对象,然而对象自身能够批改。
  2. final 润饰的办法不能被子类重写
  3. final 润饰的类不能被继承。

object 罕用办法

Java 面试常常会呈现的一道题目,Object 的罕用办法。上面给大家整顿一下。

object 罕用办法有:toString()、equals()、hashCode()、clone()等。

toString

默认输入对象地址。

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public static void main(String[] args) {System.out.println(new Person(18, "程序员大彬").toString());
    }
    //output
    //me.tyson.java.core.Person@4554617c
}

能够重写 toString 办法,依照重写逻辑输入对象值。

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {return name + ":" + age;}

    public static void main(String[] args) {System.out.println(new Person(18, "程序员大彬").toString());
    }
    //output
    // 程序员大彬:18
}

equals

默认比拟两个援用变量是否指向同一个对象(内存地址)。

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
       this.age = age;
       this.name = name;
    }

    public static void main(String[] args) {
        String name = "程序员大彬";
        Person p1 = new Person(18, name);
        Person p2 = new Person(18, name);

        System.out.println(p1.equals(p2));
    }
    //output
    //false
}

能够重写 equals 办法,依照 age 和 name 是否相等来判断:

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {if (o instanceof Person) {Person p = (Person) o;
            return age == p.age && name.equals(p.name);
        }
        return false;
    }

    public static void main(String[] args) {
        String name = "程序员大彬";
        Person p1 = new Person(18, name);
        Person p2 = new Person(18, name);

        System.out.println(p1.equals(p2));
    }
    //output
    //true
}

hashCode

将与对象相干的信息映射成一个哈希值,默认的实现 hashCode 值是依据内存地址换算进去。

public class Cat {public static void main(String[] args) {System.out.println(new Cat().hashCode());
    }
    //out
    //1349277854
}

clone

java 赋值是复制对象援用,如果咱们想要失去一个对象的正本,应用赋值操作是无奈达到目标的。Object 对象有个 clone()办法,实现了对

象中各个属性的复制,但它的可见范畴是 protected 的。

protected native Object clone() throws CloneNotSupportedException;

所以实体类应用克隆的前提是:

  • 实现 Cloneable 接口,这是一个标记接口,本身没有办法,这应该是一种约定。调用 clone 办法时,会判断有没有实现 Cloneable 接口,没有实现 Cloneable 的话会抛异样 CloneNotSupportedException。
  • 笼罩 clone()办法,可见性晋升为 public。
public class Cat implements Cloneable {
    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {Cat c = new Cat();
        c.name = "程序员大彬";
        Cat cloneCat = (Cat) c.clone();
        c.name = "大彬";
        System.out.println(cloneCat.name);
    }
    //output
    // 程序员大彬
}

浅拷贝

拷⻉对象和原始对象的引⽤类型援用同⼀个对象。

以下例子,Cat 对象外面有个 Person 对象,调用 clone 之后,克隆对象和原对象的 Person 援用的是同一个对象,这就是浅拷贝。

public class Cat implements Cloneable {
    private String name;
    private Person owner;

    @Override
    protected Object clone() throws CloneNotSupportedException {return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {Cat c = new Cat();
        Person p = new Person(18, "程序员大彬");
        c.owner = p;

        Cat cloneCat = (Cat) c.clone();
        p.setName("大彬");
        System.out.println(cloneCat.owner.getName());
    }
    //output
    // 大彬
}

深拷贝

拷贝对象和原始对象的援用类型援用不同的对象。

以下例子,在 clone 函数中不仅调用了 super.clone,而且调用 Person 对象的 clone 办法(Person 也要实现 Cloneable 接口并重写 clone 办法),从而实现了深拷贝。能够看到,拷贝对象的值不会受到原对象的影响。

public class Cat implements Cloneable {
    private String name;
    private Person owner;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Cat c = null;
        c = (Cat) super.clone();
        c.owner = (Person) owner.clone();// 拷贝 Person 对象
        return c;
    }

    public static void main(String[] args) throws CloneNotSupportedException {Cat c = new Cat();
        Person p = new Person(18, "程序员大彬");
        c.owner = p;

        Cat cloneCat = (Cat) c.clone();
        p.setName("大彬");
        System.out.println(cloneCat.owner.getName());
    }
    //output
    // 程序员大彬
}

getClass

返回此 Object 的运行时类,罕用于 java 反射机制。

public class Person {
    private String name;

    public Person(String name) {this.name = name;}

    public static void main(String[] args) {Person p = new Person("程序员大彬");
        Class clz = p.getClass();
        System.out.println(clz);
        // 获取类名
        System.out.println(clz.getName());
    }
    /**
     * class com.tyson.basic.Person
     * com.tyson.basic.Person
     */
}

equals()和 hashcode()的关系

equals 与 hashcode 的关系:
1、如果两个对象调用 equals 比拟返回 true,那么它们的 hashCode 值肯定要雷同;
2、如果两个对象的 hashCode 雷同,它们并不一定雷同。

hashcode 办法次要是用来晋升对象比拟的效率,先进行 hashcode()的比拟,如果不雷同,那就不用在进行 equals 的比拟,这样就大大减少了 equals 比拟的次数,当比拟对象的数量很大的时候能晋升效率。

之所以重写 equals()要重写 hashcode(),是为了保障 equals()办法返回 true 的状况下 hashcode 值也要统一,如果重写了 equals()没有重写 hashcode(),就会呈现两个对象相等但 hashcode()不相等的状况。这样,当用其中的一个对象作为键保留到 hashMap、hashTable 或 hashSet 中,再以另一个对象作为键值去查找他们的时候,则会查找不到。

== 和 equals 区别

  • 对于根本数据类型,== 比拟的是他们的值。根本数据类型没有 equal 办法;
  • 对于复合数据类型,== 比拟的是它们的寄存地址 (是否是同一个对象)。equals() 默认比拟地址值,重写的话依照重写逻辑去比拟。

面向对象

面向对象个性

面向对象四大个性:封装,继承,多态,形象

  • 封装就是将类的信息暗藏在类外部,不容许内部程序间接拜访,而是通过该类的办法实现对暗藏信息的操作和拜访。良好的封装可能缩小耦合。
  • 继承是从已有的类中派生出新的类,新的类继承父类的属性和行为,并能扩大新的能力,大大增加程序的重用性和易维护性。在 Java 中是单继承的,也就是说一个子类只有一个父类。
  • 多态是同一个行为具备多个不同表现形式的能力。在不批改程序代码的状况下扭转程序运行时绑定的代码。
    实现多态的三要素:继承、重写、父类援用指向子类对象。
    动态多态性:通过重载实现,雷同的办法有不同的參数列表,能够依据参数的不同,做出不同的解决。
    动静多态性:在子类中重写父类的办法。运行期间判断所援用对象的理论类型,依据其理论类型调用相应的办法。
  • 形象。把客观事物用代码形象进去。

多态怎么实现

Java 提供了编译时多态和运行时多态两种多态机制。编译时多态通过重载实现,依据传入参数不同调用不同的办法。运行时多态通过重写来实现,在子类中重写父类的办法,运行期间判断所援用对象的理论类型,依据其理论类型调用相应的办法。

类与对象

类 class对一类事物的形容,是形象的、概念上的定义。类的构造包含属性和办法。

public class Person {
    // 属性
    private int age;
    private String name;

    // 构造方法
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

对象是理论存在的该类事物的个体,也称为实例。

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public static void main(String[] args) {
       // p 为对象
       Person p = new Person(18, "程序员大彬");
    }
}

在 Java 中,万物皆对象,所有事物都能够看做对象。上述代码通过 new 关键字,创立了 Person 对象,p 为对象的援用,p 指向所创立的 Person 对象的内存地址。

属性

属性用来形容对象的状态信息,通常以变量的模式进行定义。变量分为成员变量和局部变量。

在类中,办法体之外定义的变量称为成员变量:

  • 成员变量定义在类中,在整个类中都能够被拜访
  • 成员变量分为类变量和实例变量,实例变量存在于每个对象所在的堆内存中。实例变量要创建对象后能力拜访。
  • 成员变量有默认初始化值
  • 成员变量的权限修饰符能够指定

定义在办法内,代码块内的变量称为局部变量:

  • 局部变量存在于栈内存中
  • 局部变量作用范畴完结,变量的内存空间回主动开释
  • 局部变量没有默认值,每次必须显示初始化
  • 局部变量申明时不指定权限修饰符

办法

形容的是对象的动作信息,办法也称为函数。

一般办法

一般通办法的语法结构:

[修饰符列表] 返回值类型  办法名(形参列表){// 办法体}

定义方法能够将性能代码进行封装。便于该性能进行复用。办法只有被调用才会被执行。

构造方法

构造方法是一种比拟非凡的办法,通过构造方法能够创建对象以及初始化实例变量。实例变量没有手动赋值的时候,零碎会赋默认值。

public class Person {
    // 属性
    private int age;
    private String name;

    // 构造方法
    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

办法重载

同个类中的多个办法能够有雷同的办法名称,然而有不同的参数列表,这就称为办法重载。参数列表又叫参数签名,包含参数的类型、参数的个数、参数的程序,只有有一个不同就叫做参数列表不同。

重载是面向对象的一个根本个性。

public class OverrideTest {void setPerson() { }
    
    void setPerson(String name) {//set name}
    
    void setPerson(String name, int age) {//set name and age}
}

办法重写

办法的重写形容的是父类和子类之间的。当父类的性能无奈满足子类的需要,能够在子类对办法进行重写。

办法重写时,办法名与形参列表必须统一。

如下代码,Person 为父类,Student 为子类,在 Student 中重写了 dailyTask 办法。

public class Person {
    private String name;
    
    public void dailyTask() {System.out.println("work eat sleep");
    }
}


public class Student extends Person {
    @Override
    public void dailyTask() {System.out.println("study eat sleep");
    }
}

初始化程序

Java 中类初始化程序:

  1. 动态属性,动态代码块。
  2. 一般属性,一般代码块。
  3. 构造方法。
public class LifeCycle {
    // 动态属性
    private static String staticField = getStaticField();

    // 动态代码块
    static {System.out.println(staticField);
        System.out.println("动态代码块初始化");
    }

    // 一般属性
    private String field = getField();

    // 一般代码块
    {System.out.println(field);
        System.out.println("一般代码块初始化");
    }

    // 构造方法
    public LifeCycle() {System.out.println("构造方法初始化");
    }

    // 静态方法
    public static String getStaticField() {
        String statiFiled = "动态属性初始化";
        return statiFiled;
    }

    // 一般办法
    public String getField() {
        String filed = "一般属性初始化";
        return filed;
    }

    public static void main(String[] argc) {new LifeCycle();
    }

    /**
     *      动态属性初始化
     *      动态代码块初始化
     *      一般属性初始化
     *      一般代码块初始化
     *      构造方法初始化
     */
}

接口和抽象类

Java 中接口和抽象类的定义语法别离为 interface 与 abstract 关键字。

抽象类

首先理解一下形象办法。形象办法是一种非凡的办法:它只有申明,而没有具体的实现。形象办法的格局为:

abstract void method();

形象办法必须用 abstract 关键字进行润饰。如果一个类含有形象办法,则称这个类为抽象类,抽象类必须在类前用 abstract 关键字润饰。因为抽象类中含有未实现的办法,所以不能用抽象类创建对象。

抽象类的特点:

  • 抽象类不能被实例化只能被继承;
  • 蕴含形象办法的肯定是抽象类,然而抽象类不肯定含有形象办法;
  • 抽象类中的形象办法的修饰符只能为 public 或者 protected,默认为 public。

接口

接口对行为的形象。在 Java 中,定一个接口的模式如下:

public interface InterfaceName {}

接口中能够含有变量和办法。

接口的特点:

  • 接口中的变量会被隐式指定为 public static final 类型,而办法会被隐式地指定为 public abstract 类型;
  • 接口中的办法必须是形象办法,所有的办法不能有具体的实现;
  • 一个类能够实现多个接口。

接口的用途

接口是对行为的形象,通过接口能够实现不相干类的雷同行为。通过接口能够晓得一个类具备哪些行为个性。

接口与抽象类区别

  • 语法层面上
    1)抽象类能够有办法实现,而接口的办法中只能是形象办法;
    2)抽象类中的成员变量能够是各种类型的,接口中的成员变量只能是 public static final 类型;
    3)接口中不能含有动态代码块以及静态方法,而抽象类能够有动态代码块和静态方法;
    4)一个类只能继承一个抽象类,而一个类却能够实现多个接口。
  • 设计层面上的区别
    1)抽象层次不同。抽象类是对整个类整体进行形象,包含属性、行为,然而接口只是对类行为进行形象。继承抽象类是一种 ” 是不是 ” 的关系,而接口实现则是 “ 有没有 ” 的关系。如果一个类继承了某个抽象类,则子类必然是抽象类的品种,而接口实现则是具备不具备的关系,比方鸟是否能飞。
    2)继承抽象类的是具备类似特点的类,而实现接口的却能够不同的类。

    门和警报的例子:

    class AlarmDoor extends Door implements Alarm {//code}
    
    class BMWCar extends Car implements Alarm {//code}

反射

Java 反射就是在运行状态中,对于任意一个类,都可能晓得这个类的所有属性和办法;对于任意一个对象,都可能调用它的任意办法和属性,并且能扭转它的属性。

作用:能够更灵便的编写代码,代码能够在运行时拆卸,无需在组件之间进行源代码链接,升高代码的耦合度;还有动静代理的实现等。须要留神的是反射使用不当会造成很高的资源耗费!

Class 类

在 Java 中,每定义一个 Java class 实体都会产生一个 Class 对象。这个 Class 对象用于示意这个类的类型信息。

获取 Class 对象的三种形式:

//1、通过对象调用 getClass() 办法来获取, 通常利用在:比方你传过来一个 Object
//  类型的对象,而我不晓得你具体是什么类,用这种办法
  Person p1 = new Person();
  Class c1 = p1.getClass();

//2、间接通过 类名.class 的形式失去, 该办法最为安全可靠,程序性能更高
//  这阐明任何一个类都有一个隐含的动态成员变量 class
  Class c2 = Person.class;

//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,//   但可能抛出 ClassNotFoundException 异样
  Class c3 = Class.forName("com.ys.reflex.Person");

Class 类提供了一些办法,能够获取成员变量、成员办法、接口、超类、构造方法:

  getName():取得类的残缺名字。getFields():取得类的 public 类型的属性。getDeclaredFields():取得类的所有属性。包含 private 申明的和继承类
  getMethods():取得类的 public 类型的办法。getDeclaredMethods():取得类的所有办法。包含 private 申明的和继承类
  getMethod(String name, Class[] parameterTypes):取得类的特定办法,name 参数指定办法的名字,parameterTypes 参数指定办法的参数类型。getConstructors():取得类的 public 类型的构造方法。getConstructor(Class[] parameterTypes):取得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。newInstance():通过类的不带参数的构造方法创立这个类的一个对象。

Field 类

Field 提供了类和接口中字段的信息,通过 Field 类能够动静拜访这些字段。下图是 Field 类提供的一些办法。

Method 类

Method 类位于 java.lang.reflect 包中,次要用于在程序运行状态中,动静地获取办法信息。

Method 类罕用的办法:

  1. getAnnotation(Class<T> annotationClass):如果该办法对象存在指定类型的注解,则返回该注解,否则返回 null。
  2. getName():返回办法对象名称。
  3. isAnnotationPresent(Class<? extends Annotation> annotationClass):如果该办法对象上有指定类型的注解,则返回 true,否则为 false。
  4. getDeclaringClass ():返回该办法对象示意的办法所在类的 Class 对象。
  5. getParameters():返回一个参数对象数组,该数组示意该办法对象的所有参数。
  6. getReturnType():返回一个 Class 对象,该 Class 对象示意该办法对象的返回对象,会擦除泛型。

泛型

泛型的实质是参数化类型,也就是说所操作的数据类型被指定为一个参数。编译时会进行类型擦除。在泛型应用过程中,操作的数据类型被指定为一个参数,这种参数类型能够用在类、接口和办法中,别离被称为泛型类、泛型接口、泛型办法。

泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型能够实现对一组类的操作对外开放雷同的接口。最典型的就是各种容器类,如:List、Set、Map。

泛型类示例:

// 此处 T 能够轻易写为任意标识,常见的如 T、E、K、V 等模式的参数罕用于示意泛型
// 在实例化泛型类时,必须指定 T 的具体类型
public class Generic<T>{ 
    //key 这个成员变量的类型为 T,T 的类型由内部指定  
    private T key;

    public Generic(T key) { // 泛型构造方法形参 key 的类型也为 T,T 的类型由内部指定
        this.key = key;
    }

    public T getKey(){ // 泛型办法 getKey 的返回值类型为 T,T 的类型由内部指定
        return key;
    }
}

泛型类的应用:

@Slf4j
public class GenericTest {public static void main(String[] args) {
        // 泛型的类型参数只能是类类型(包含自定义类),不能是简略类型
        // 传入的实参类型需与泛型的类型参数类型雷同,即为 Integer.
        Generic<Integer> genericInteger = new Generic<Integer>(666);

        // 传入的实参类型需与泛型的类型参数类型雷同,即为 String.
        Generic<String> genericString = new Generic<String>("程序员大彬");
        log.info("泛型测试: key is {}", genericInteger.getKey());
        log.info("泛型测试: key is {}", genericString.getKey());
    }

    /**
     * output
     * 23:51:55.519 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 666
     * 23:51:55.526 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 程序员大彬
     */
}

泛型接口

泛型接口与泛型类的定义及应用基本相同。

// 定义一个泛型接口
public interface Generator<T> {public T next();
}

泛型办法

泛型类,是在实例化类的时候指明泛型的具体类型;泛型办法,是在调用办法的时候指明泛型的具体类型。

@Slf4j
public class GenericMethod {

    /**
     * 泛型办法的根本介绍
     * @param t 传入的泛型实参
     * @return T 返回值为 T 类型
     * 阐明:*     1)public 与 返回值两头 <T> 十分重要,能够了解为申明此办法为泛型办法。*     2)只有申明了 <T> 的办法才是泛型办法,泛型类中的应用了泛型的成员办法并不是泛型办法。*     3)<T> 表明该办法将应用泛型类型 T,此时才能够在办法中应用泛型类型 T。*     4)与泛型类的定义一样,此处 T 能够轻易写为任意标识,常见的如 T、E、K、V 等模式的参数罕用于示意泛型。*/
    public <T> void genericMethod(T t) {log.info(t.toString());
    }

    public static void main(String[] args) {GenericMethod genericMethod = new GenericMethod();
        genericMethod.genericMethod("程序员大彬");
        genericMethod.genericMethod(666);
    }

    /**
     * output
     * 23:59:11.906 [main] INFO com.tyson.generic.GenericMethod - 程序员大彬
     * 23:59:11.912 [main] INFO com.tyson.generic.GenericMethod - 666
     */
}

Exception

在 JAVA 语言中,将程序执行中产生的不失常状况称为异样。异样是程序常常会呈现的状况,发现错误的最佳时机是在编译阶段,也就是运行程序之前。

所有异样都继承了 Throwable。

Throwable

Throwable 类是 Error 和 Exception 的父类,只有继承于 Throwable 的类或者其子类能力被抛出。Throwable 分为两类:

  • Error:JVM 无奈解决的重大问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无奈解决的谬误。

    栈溢出:如下代码递归调用 main,最终会抛出 StackOverflowError。

    public class Test {public static void main(String[] args) {main(args);
        }
        /**
         * Exception in thread "main" java.lang.StackOverflowError
         */
    }

    内存溢出 OOM:上面的代码中,新建了一个数组,数组长度是 1G,又因为是 Integer 包装类型,一个元素占 4 个字节,所以,这个数组占用内存 4GB。最初,堆内存空间有余,抛出了 OOM。

    public class Test {public static void main(String[] args) {Integer[] arr = new Integer[1024 * 1024 * 1024];
        }
        /**
         * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
         */
    }
  • Exception:其它因编程谬误或偶尔的外在因素导致的一般性问题。能够在代码中进行解决。如:空指针异样、数组下标越界等。

unchecked exception 包含 RuntimeException 和 Error 类,其余所有异样称为查看(checked)异样。

运行时异样和非运行时异样(checked)的区别:

  1. RuntimeException 由程序谬误导致,应该修改程序防止这类异样产生。
  2. checked Exception 由具体的环境(读取的文件不存在或文件为空或 sql 异样)导致的异样。必须进行解决,不然编译不通过,能够 catch 或者 throws。

常见的 Exception

常见的 RuntimeException:

ClassCastException // 类型转换异样
IndexOutOfBoundsException // 数组越界异样
NullPointerException // 空指针
ArrayStoreException // 数组存储异样
NumberFormatException // 数字格式化异样
ArithmeticException // 数学运算异样

unchecked Exception:

NoSuchFieldException // 反射异样,没有对应的字段
ClassNotFoundException // 类没有找到异样
IllegalAccessException // 平安权限异样,可能是反射时调用了 private 办法

关键字

  • throw:用于抛出一个具体的异样对象。
  • throws:用在办法签名中,用于申明该办法可能抛出的异样。子类办法抛出的异样范畴更加小,或者基本不抛异样。
  • try:用于监听。将可能抛出异样的代码放在 try 语句块之中,当 try 语句块内产生异样时,异样就被抛出。
  • catch:用于捕捉异样。
  • finally:finally 语句块总是会被执行。它次要用于回收在 try 块里关上的资源(如数据库连贯、网络连接和磁盘文件)。

如下代码示例,除以 0 抛出异样,产生异样之后的代码不会执行,间接跳到 catch 语句块执行,最初执行 finally 语句块。

public class ExceptionTest {public static void main(String[] args) {
        try {
            int i = 1 / 0;
            System.out.println(i + 1);
        } catch (Exception e) {System.out.println(e.getMessage());
        } finally {System.out.println("run finally...");
        }
    }
    /**
     * / by zero
     * run finally...
     */
}

IO 流

Java IO 流的外围就是对文件的操作,对于字节、字符类型的输出和输入流。IO 流次要分为两大类,字节流和字符流。字节流能够解决任何类型的数据,如图片,视频等,字符流只能解决字符类型的数据。

图片参考:Java io 学习整顿

InputStream 和 OutputStream

InputStream 用来示意那些从不同数据源产生输出的类。这些数据源包含:1. 字节数组;2.String 对象;3. 文件;4. 管道;5. 一个由其余品种的流组成的序列。

InputStream 类有一个形象办法:abstract int read(),这个办法将读入并返回一个字节,或者在遇到输出源结尾时返回 -1。

OutputStream 决定了输入所要去的指标:字节数组、文件或管道。OutputStream 的 abstract void write(int b) 能够向某个输入地位写出一个字节。

read() 和 write() 办法在执行时都将阻塞,期待数据被读入或者写出。

罕用的字节流有 FileInputStream、FileOutputStream、ObjectInputStream、ObjectOutputStream。

Reader 和 Writer

字符流是由通过字节流转换失去的,转化过程耗时,而且容易呈现乱码问题。I/O 流提供了一个间接操作字符的接口,不便咱们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比拟好,如果波及到字符的话应用字符流比拟好。

abstract int read();
abstract void write(char c);

字符流和字节流的转换

InputStreamReader:字节到字符的转换,可对读取到的字节数据通过指定编码转换成字符。

OutputStreamWriter:字符到字节的转换,可对读取到的字符数据通过指定编码转换成字节。

同步异步

同步:收回一个调用时,在没有失去后果之前,该调用就不返回。

异步:在调用收回后,被调用者返回后果之后会告诉调用者,或通过回调函数解决这个调用。

阻塞非阻塞

阻塞和非阻塞关注的是线程的状态。

阻塞调用是指调用后果返回之前,以后线程会被挂起。调用线程只有在失去后果之后才会复原运行。

非阻塞调用指在不能立即失去后果之前,该调用不会阻塞以后线程。

举个例子,了解下同步、阻塞、异步、非阻塞的区别:

同步就是烧开水,要本人来看开没开;异步就是水开了,而后水壶响了告诉你水开了(回调告诉)。阻塞是烧开水的过程中,你不能干其余事件,必须在旁边等着;非阻塞是烧开水的过程里能够干其余事件。

BIO

同步阻塞 I / O 模式,数据的读取写入必须阻塞在一个线程内期待其实现。

NIO

NIO 是一种同步非阻塞的 I / O 模型,在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等形象。

NIO 与 IO 区别:

  • IO 是面向流的,NIO 是面向缓冲区的;
  • IO 流是阻塞的,NIO 流是不阻塞的;
  • NIO 有选择器,而 IO 没有。

Buffer:Buffer 用于和 Channel 交互。从 Channel 中读取数据到 Buffer 里,从 Buffer 把数据写入到 Channel。

Channel:NIO 通过 Channel(通道)进行读写。通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和 Buffer 交互。

Selector:应用更少的线程来就能够来解决通道了,相比应用多个线程,防止了线程上下文切换带来的开销。

AIO

异步非阻塞 IO。异步 IO 是基于事件和回调机制实现的,也就是利用操作之后会间接返回,不会梗塞在那里,当后盾解决实现,操作系统会告诉相应的线程进行后续的操作。

BIO/NIO/AIO 区别

同步阻塞 IO : 用户过程发动一个 IO 操作当前,必须期待 IO 操作的真正实现后,能力持续运行。

同步非阻塞 IO: 客户端与服务器通过 Channel 连贯,采纳多路复用器轮询注册的 Channel。进步吞吐量和可靠性。用户过程发动一个 IO 操作当前,可做其它事件,但用户过程须要轮询 IO 操作是否实现,这样造成不必要的 CPU 资源节约。

异步非阻塞 IO: 非阻塞异步通信模式,NIO 的升级版,采纳异步通道实现异步通信,其 read 和 write 办法均是异步办法。用户过程发动一个 IO 操作,而后立刻返回,等 IO 操作真正的实现当前,应用程序会失去 IO 操作实现的告诉。类比 Future 模式。

文章很长,花了挺长时间整顿的,如果感觉对你有帮忙,能够点个赞激励一下~

我是程序员大彬,专一 Java 后盾开发常识分享,欢送大家关注~

正文完
 0