Java笔记01——JAVA基础部分

一、Java相关概念:

1、Java的特点:

(1)面向对象。(2)跨平台性。(3)多线程。(4)安全可靠。(5)自动回收内存。

2、JVM & JDK &JRE:

(1)JVM:

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java的运行过程如下:

.java(源代码)-------->.class(字节码)-------->机器码

JVM可以将.class文件转换为针对不同平台(Windows,Linux)的机器码,从而保证一套Java代码可以在不同的平台上运行。保证了Java的跨平台性。

在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比较高效,而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。

Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。

(2)JDK:

JDK是Java Development Kit(Java开发工具包)的缩写,包含整套的Java运行环境(JRE),以及其他的Java库文件,Java工具包等。

(3)JRE:

JRE是Java Runtime Envirnment(Java运行环境)的缩写,是运行java程序的最低要求。

3、Java是编译与解释并存的:

编程语言分为编译和解释两种,

编译就是将代码一次性翻译能底层硬件能理解的机器码。

解释就是指解释器将代码逐行解释成机器码并运行。

Java先通过编译生成.class字节码文件。然后在通过JVM解释成机器码。所以Java是编译和解释并存的。

4、Java与C++的区别:

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
  • C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)。

Import java和javax是同一个东西.

二、Java基础:

1、标识符:

(1)只能用字母、数字、下划线、美元符号组成!!!!

(2)数字不能开头。

(3)可以用_和$开头。

(4)严格区分大小写。

(5)不能与关键字相同。

补充:根据《阿里巴巴Java开发手册》的编程规范,要求代码的命名不能以下划线和美元符号开始,也不能以下划线和美元符号结束。

反例:_name/name_/$name/name$。我们应当遵守行业的规范要求。

2、字符常量和字符串常量的区别:

字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。

字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。

字符常量只占 2 个字节; 字符串常量占若干个字节 。

3、Java基本类型:

基本类型的字面值:

1、整数的字面值是int;

2、byte,short,char 三种比int小的类型,可以直接用范围内的字面值。

3、浮点数的字面值是double.

4、字面值后缀

long   L

float   F

double D

5、字面值前缀

二进制 0b

16进制 0X

8进制 0

char类型16进制 \u

基本类型的运算规则:

1、整型运算溢出

2.byte short char 在运算时会转成int

3.结果的类型,与运算项中最大的类型一致

4.浮点数运算不精确  (用BigDecimal)

5.浮点数的两个特殊值

-   Infinity      无穷   (浮点数/0)

-   NaN         不是个数    (用负数开平方)

4、注释:

Java中的注释有三种:

注:代码的注释不是越详细越好,最好的注释就是代码本身,要加强代码自身的可读性。

5、标识符和关键字的区别:

简单来说,标识符就是类,对象或变量的名字。当某些标识符被赋予了其他的意义和作用时,它就是一个关键字。关键字是Java内已经存在的,数量有限且不能与标识符重名的。

Java中常见的关键字:

访问控制privateprotectedpublic
类,方法和变量修饰符abstractclassextendsfinalimplementsinterfacenative
newstaticstrictfpsynchronizedtransientvolatile
程序控制breakcontinuereturndowhileifelse
forinstanceofswitchcasedefault
错误处理trycatchthrowthrowsfinally
包相关importpackage
基本类型booleanbytechardoublefloatintlong
shortnulltruefalse
变量引用superthisvoid
保留字gotoconst

6、return,continue,break的区别:

在循环过程中,当循环条件不满足时跳出循环,当我们想要在循环过程中进行一些操作:提前跳出循环,跳过本次循环,结束运行等,就要使用上述关键字。

return:

(1)在方法中用于返回值,return 0;

(2)return直接使用将会结束方法的执行;

continue:

跳过本次循环,开始下一次循环。

break:

跳出循环。

7、泛型是什么?为什么说Java是伪泛型?常见的通配符有那些?

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

答案是可以使用 Java 泛型

使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。

Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。

常见的通配符:

E-element(在集合中使用)

V-value

K-key

N-number

?-表示不确定的java类型

8、==和equals()

(1)==:

对于基本数据类型来说,==比较的是数值,对于引用数据类型来说,==比较的是地址。要注意的是,引用类型比较的是地址,是因为引用类型本身存储的就是一个地址。

(2)public boolean equals​(Object obj):

如果此对象与obj参数相同=>true;否则为false;

底层源码:

public boolean equals(Object obj) {
    return (this == obj);
}

如果不重写,默认比较两个对象的引用地址。

举个例子:

 public static void main(String[] args) {
        User user4 = null;
        User user1 = new User(24,"bob","男");
        User user2 = new User(23,"alice","女");
        User user3 = new User(24,"bob","男");
        //System.out.println(user4.equals(user2));//error 报空指针异常
        System.out.println(user1.equals(user2));
        System.out.println(user1.equals(user3));
        //推荐使用 java.util.Objects#equals(JDK7 引入的工具类)。
        System.out.println(Objects.equals(user1,user2));
    }
}
class User{
    int age = 0;
    String name = "";
    String gender = "";
    public User(int age,String name,String gender){
        this.age = age;
        this.name = name;
        this.gender = gender;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name) && Objects.equals(gender, user.gender);
    }
}

三、Java基本类型:

1、包装类:

基本类型转化为包装类型主要是为了调用包装类的方法
* 包装类具有以下特性:
* 1、Byte类的所有对象地址都是固定的
* 2、short,int,long,如果是-128~127范围内,那么对象地址是固定的;如果超出范围,那么会创建新的对象。
* 3、浮点类型直接创建新的对象。
* 4、Character类,如果小于或等于127,返回固定对象,其余情况直接创建新的对象。
  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

举个例子:

//装箱:将基本类型转换为包装类型
        //创建方式1  每次new一个对象时都会开辟一个新的内存空间
        Integer i = new Integer(10);
        //创建方式2  和String类似,当数据的值相同时,指向常量池中的同一个地址
        Integer i2= Integer.valueOf(10);
        Integer i9= Integer.valueOf(10);
        System.out.println("valueOf分配的空间" + (i2 == i9));
        System.out.println(i);
        System.out.println(i2);
        System.out.println(i.getClass());
        //拆箱:将包装类型转换为基本类型
        int i3= i.intValue();
        System.out.println(i3);
        //自动装箱和拆箱:
        /**
         * Integer i = 10 等价于 Integer i = Integer.valueOf(10)
         * int n = i 等价于 int n = i.intValue();
         * **/
        Integer i4 = 15;//相当于 Integer i4 = Integer.valueOf(15)
         int i5 = i;
        System.out.println(i4);
        System.out.println(i5);

        Integer i6 = 12;
        System.out.println(i6.getClass());
        Integer i7 = 12;
        Integer i8 = 13;
        Integer i10 = 128;
        Integer i11 = 128;
        System.out.println(i6 == i7);//true
        System.out.println(i6 == i8);
        System.out.println(i6 == i10);
        System.out.println(i10 == i11);//false
        Short s1 = Short.valueOf("5");
        System.out.println(s1);
    }

数组:

int[] a = new int[5];

int[] a = {1,2,3,4,5};

a = new int[]{1,2,3,4,5};

数组长度不可变

a.length长度属性

允许0长度的数组

四、方法(函数):

1、什么是函数的返回值?

函数的返回值就是指函数运行返回的结果。其作用是获得函数的运行结果,去进行其他的操作。

2、函数的类型:

(1)无参数无返回值。(2)有参数无返回值。(3)无参数有返回值。(4)有参数有返回值。

3、Static和final:

(1)Static:

1、可以修饰成员变量,成员方法

2、 随着类的加载而加载,优先于对象加载

3、 只加载一次,就会一直存在,不再开辟新空间

4、 全局唯一,全局共享

5、 可以直接被类名调用

6、 静态只能调用静态,非静态可以随意调用

7、 static不能和this或者super共用,因为有static时可能还没有对象

(2)final:

1、 被final修饰的类,不能被继承

2、 被final修饰的方法,不能被重写

3、 被final修饰的变量是个常量,值不能被更改

4、 常量的定义形式: final 数据类型 常量名 = 值

4、 在一个静态方法内调用非静态成员变量为什么是错误的?

静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,然后通过类的实例对象去访问。在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。

5、静态方法和实例方法:

(1)在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

(2)静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

6、重载和重写:

(1)重载:重载是指同一个方法名的多个方法,针对不同的参数进行不同的操作。一般发生在同一个类中(或者父类和子类之间),方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。

(2)重写:一般发生在父类和子类之间,子类可以继承父类的方法,并且重写此方法来满足自己的需要。返回值类型、方法名、参数列表必须相同,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。构造方法无法被重写。

注意:private修饰的变量和方法依然被子类继承了,只不过无法使用。

7.方法递归

调用自身,一步一步简化问题,把问题简化为最简问题,再倒推求出结果

五、面向对象:

1、面向对象和面向过程:

面向过程 :面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
面向对象 :面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低。

2、创建对象:

Person p = new Person();//短短这行代码发生了很多事情
1.把Person.class文件加载进内存
2.在栈内存中,开辟空间,存放引用变量p
3.在堆内存中,开辟空间,存放Person对象
4.对成员变量进行默认的初始化
5.对成员变量进行显示初始化
6.执行构造方法(如果有构造代码块,就先执行构造代码块再执行构造方法)
7.堆内存完成
8.把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值

3、封装、继承和多态:

(1)封装:

封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。

举个例子:

class Person{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

(2)继承:

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。

Java的继承有以下特点:

1、子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
2、子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
3、子类可以用自己的方式实现父类的方法。
4、Java的继承是单继承,即一个类只能有一个父类。

(3)多态:

多态,顾名思义,表示一个对象具有多种的状态。具体表现为父类的引用指向子类的实例。

Java多态的前提:

(1)有继承。(2)有方法的重写
Java多态的特点:
(1)多态的重要特点是动态定义。Java的多态中,方法是动态定义的,而变量不是动态定义的。看一个例子:

public static void main(String[] args) {
        Person alice = new Student02();
        alice.eat();
        System.out.println(alice.name);
    }
}
class Person{
    String name = "bob";
    int age = 23;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(name+"正在吃饭");
    }
}
class Student02 extends Person{
    String name = "alice";
    int age = 24;

    public Student02() {
    }

    public Student02(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name+"同学正在吃饭");
    }
}

输出结果为:

alice同学正在吃饭
bob

可以看到,eat方法被动态定义成了Student02的方法。但是属性依然是父类的属性。

4、构造函数:

构造方法主要作用是完成对类对象的初始化工作。

特点:

名字与类名相同。
没有返回值,但不能用 void 声明构造函数。
生成类的对象时自动执行,无需调用。
构造方法不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

如果一个类没有声明构造方法,也可以执行!因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。如果我们自己添加了类的构造方法(无论是否有参),Java 就不会再添加默认的无参数的构造方法了,这时候,就不能直接 new 一个对象而不传递参数了,所以我们一直在不知不觉地使用构造方法,这也是为什么我们在创建对象的时候后面要加一个括号(因为要调用无参的构造方法)。如果我们重载了有参的构造方法,记得都要把无参的构造方法也写出来(无论是否用到),因为这可以帮助我们在创建对象的时候少踩坑。

5、代码块(构造代码块、局部代码块、静态代码块和同步代码块):

代码块是指被“{ }”包含的代码集合,分为上述的四种:

(1)局部代码块

定义:在方法、循环、判断等语句中出现的代码块
修饰:只能用标签修饰
位置:普通代码块可以出现在方法体内除"()"外的任何地方,包括 方法体,代码块中(即可以嵌套在代码块中)
执行:普通代码依赖方法的执行而执行,按照正常的先后顺序执行

(2)构造代码块

定义:在类中定义且没有加任何修饰的代码块
位置:它不能出现在方法体或者其他代码块内 
执行:依赖于构造函数的调用而执行
作用:初始化实例变量和实例环境,一般用于提取类构造函数中的公共代码
注意:构造代码块不是在构造函数之前执行的!!!编译器在编译的时候会把构造代码块插入到每个构造函数的最前面!!
     构造代码块随着构造函数的执行而执行!!
     如果某个构造函数调用了其他的构造函数,那么构造代码块不会插入到该构造函数中以免构造代码块执行多次! 

(3)静态代码块

定义:在类中定义使用static修饰的代码块
 修饰:使用static修饰
 位置:它不能出现在方法体或者代码块内 
 执行:在加载类时会先执行静态代码块,且只执行一次,如果有多个静态代码块则按照先后顺序执行
 作用:一般用于静态变量的初始化,创建对象前的环境的加载
 注意:静态代码块中不能直接访问非静态变量和方法,需要通过类的实例对象来访问 

(4)同步代码块:

定义:可以简单地认为同步代码块是使用 synchronized 修饰的普通代码块
位置:同普通代码块
执行:同普通代码块
作用:用于多线程环境的同步保护
注意:注意同步代码块使用不当可能会造成“死锁”

注:执行顺序:静态代码块 --> 构造代码块 --> 构造方法 --> 局部代码块

6、this和super:

(1)this关键字

用于引用类的当前实例:

String name = "bob";
    int age = 23;

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

用于构造方法的相互调用:

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

    public Person() {
        this("alice",23);
    }

(2)super关键字:

用于从子类访问父类的变量和方法

class Person{
    String name = "bob";
    int age = 23;

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

    }
    public Person() {
        this("alice",23);
    }
}
class Student_Test extends Person{
    public Student_Test(String name, int age) {
        super(name, age);
    }

    public Student_Test() {
    }
    public void Print_Name(){
        System.out.println(super.name);
    }
}

注:在构造器中使用 super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this 调用本类中的其他构造方法时,也要放在首行。
this、super不能用在static方法中。

7、内部类:

被定义在另一个类的内部,所以称为内部类(Nested Class)。

内部类的特点:

1) 内部类可以直接访问外部类中的成员,包括私有成员
2) 外部类要访问内部类的成员,必须要建立内部类的对象
3) 在成员位置的内部类是成员内部类
4) 在局部位置的内部类是局部内部类

public static void main(String[] args) {
        outer o1 = new outer();
        //outer.inner i1 = new outer().new inner();
    }
}

class outer{
    String name = "";
    int age = 0;
    inner in = new inner();
    public void eating(){
        System.out.println(name+" is eating");
    }
        //被私有化的内部类只能在外部类内实例化
        private class inner{
        int grade = 0;
        String gender = "女";

        @Override
        public String toString() {
            return "inner{" +
                    "grade=" + grade +
                    ", gender=" + gender +
                    '}';
        }
    }

}

六、异常:

1、异常的分类:

在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类 Exception(异常)和 Error(错误)。

Exception 能被程序本身处理(try-catch), Error 是无法处理的(只能尽量避免)。

Exception 和 Error 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 受检查异常(必须处理) 和 不受检查异常(可以不处理)。
Error :Error 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。

2、异常的处理:捕获和抛出

3、Throwable的常用方法:

public string getMessage():返回异常发生时的简要描述
public string toString():返回异常发生时的详细信息
public string getLocalizedMessage():返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()返回的结果相同
public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息

4、try-catch-finally和try-with-resources

面对必须要关闭的资源,我们总是应该优先使用 try-with-resources 而不是try-finally。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally则几乎做不到这点。

try-with-resources的结构:

try(需要关闭的资源){

操作.......

}catch(异常类型){

处理方式......

}

举个例子:

public static void copyFile(File in,File out) {
        try {
            in.createNewFile();
            out.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (BufferedInputStream buffIn = new BufferedInputStream(new FileInputStream(in));
             BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(out));)
        {
            int str = 0;
            while ((str = buffIn.read()) != -1){
                System.out.println(str);
                writer.write(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

七、API:

1、Arrays:

该类包含用于操作数组的各种方法(如排序和搜索)。

主要方法:

Arrays.asList()
将一个数组转换成一个List集合,asList的返回值是一个Arrays的内部类ArrayList,而不是java.util.ArrayList,没有重写add(),remove(),clear(),如果调用了上述方法会报错。

那么我又该如何将数组转化为ArrayList?

ArrayList list = new ArrayList<>(Arrays.asList(数组))

Arrays.toString(数组)
把数组里的数据,用逗号连接成一个字符串。

格式:[10, 14, 20, 46, 51]

 Arrays.sort(数组)
对数组排序,对于基本类型的数组使用优化后的快速排序算法,效率高。

对引用类型数组,使用优化后的合并排序算法。

Arrays.copyOf(数组,新的长度)
把数组复制成一个指定长度的新数组。

新数组长度大于原数组,相当于复制,并增加位置。--数组的扩容

新数组长度小于原数组,相当于截取前一部分数据。--数组的缩容

2、Object:

Object是类层次结构的根。 每个类都有Object作为超类。 所有对象(包括数组)都实现了这个类的方法。

protected Object clone​() 创建并返回此对象的副本。  
boolean equals​(Object obj) 指示一些其他对象是否等于此。  
Class<?> getClass​() 返回此 Object的运行时类。  
int hashCode​() 返回对象的哈希码值。  
void notify​() 唤醒正在等待对象监视器的单个线程。  
void notifyAll​() 唤醒正在等待对象监视器的所有线程。  
String toString​() 返回对象的字符串表示形式。  
void wait​() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。  
void wait​(long timeout) 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或指定的时间已过。  
void wait​(long timeout, int nanos) 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或者已经过了一定量的实时时间。  
 

这里着重再说一下equals,如果不重写,则默认比较的是两个对象的引用地址。

当一个为null的对象调用equals方法时,会报空指针错误。

当一个非null对象调用equals方法和null对象比较时,会正常返回。

举个例子:

        User user4 = null;
        User user1 = new User(24,"bob","男");
        User user2 = new User(23,"alice","女");
        User user3 = new User(24,"bob","男");
        //System.out.println(user4.equals(user2));//error 报空指针异常
        System.out.println(user1.equals(user4));//false

在JDK7中,引入了一个新的工具类 

java.util.Objects.equals

底层代码如下:

public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

使用Objects.euqals能够避免上述的空指针异常

3、StringBuffer和StringBuilder:

StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
 public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }
public StringBuffer() {
        super(16);
    }

    /**
     * Constructs a string buffer with no characters in it and
     * the specified initial capacity.
     *
     * @param      capacity  the initial capacity.
     * @exception  NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuffer(int capacity) {
        super(capacity);
    }

观察两者的方法,可以看到StringBuffer的方法使用synchronized修饰,StringBuiledr则没有:

StringBuffer:

 @Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }


    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        super.ensureCapacity(minimumCapacity);
    }

StringBuilder:

    @Override
    public StringBuilder deleteCharAt(int index) {
        super.deleteCharAt(index);
        return this;
    }

    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder replace(int start, int end, String str) {
        super.replace(start, end, str);
        return this;
    }

由此我们可以看出两者的区别在哪里:

StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

4、String:

String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以String 对象是不可变的。每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。String 中的对象是不可变的,也就可以理解为常量,线程安全。

String的构造方法:

char[] cha1 = {'h','e','l','l','o'};
        //构造方法1
        String str = "abcd";//abcd
        //构造方法2
        String str2 = new String(cha1);//hello
        //构造方法3
        String str3 = new String(cha1,1,4);//ello

String的主要方法:

(1)转换方法:

toCharArray、toLowerCase、toUpperCase、valueOf

(2)判断方法:

equals、euqalsIgnoreCase、startsWith、endsWith

(3)功能方法:

length、charAt、indexOf、substring、trim、replace、split方法

(请自行回忆方法的参数和用法及返回值)

最后补充说明一点:

使用String str = "123"创建一个数组时,java会在字符串常量池创建一个对象。

此时我又创建一个String, String str2 = "123";则JVM会将两个变量指向同一个地址。

但是如果我new了一个实例对象 String str3 = new String("123");则会在堆内存中开辟一片空间,不与上述两个公用一个地址。

举个例子:

//如果是第一次使用字符串,java会在字符串常量池创建一个对象。
        String str1 = "abc";
        //开辟一个新的内存空间,所以str2是与str1 str3不同的对象
        String str2 = new String("abc");
        //与str1是指向的同一个对象。
        String str3 = "abc";
        System.out.println(str1 == str2);//true
        System.out.println(str1 == str3);//false

5、Math:

        //PI
        System.out.println(Math.PI);
        //E
        System.out.println(Math.E);
        //绝对值
        System.out.println(Math.abs(-3.14));
        //向上取整
        System.out.println(Math.ceil(3.5));
        //向下取整
        System.out.println(Math.floor(3.5));
        //最大值
        System.out.println(Math.max(3.14,2.17));
        //最小值
        System.out.println(Math.min(3.14,2.17));
        //求A参数的B参数次幂
        System.out.println(Math.pow(5,10));
        System.out.println(Math.pow(2,3));
        //获取(0,1)的随机数
        System.out.println(Math.random());
        //四舍五入
        System.out.println(Math.round(3.6415962));

输出结果为:

3.141592653589793
2.718281828459045
3.14
4.0
3.0
3.14
2.17
9765625.0
8.0
0.5402362649156143
4

6、Date:

 在JDK 1.1之前, Date有两个附加功能。 它允许将日期解释为年,月,日,小时,分钟和第二个值。 它还允许格式化和解析日期字符串。 不幸的是,这些功能的API不适合国际化。 从JDK 1.1开始, Calendar类应用于在日期和时间字段之间进行转换,而DateFormat类应用于格式化和解析日期字符串。 Date中的相应方法Date被弃用。 

构造方法:

Date​() 分配一个 Date对象并对其进行初始化,以便它表示分配的时间,以最接近的毫秒为单位。  

Date​(long date) 分配一个 Date对象,并将其初始化为表示从标准基准时间(称为“时代”)即1970年1月1日00:00:00 GMT起的指定毫秒数。  
 

未过期的方法:

boolean after​(Date when) 测试此日期是否在指定日期之后。  
boolean before​(Date when) 测试此日期是否在指定日期之前。  
Object clone​() 返回此对象的副本。  
int compareTo​(Date anotherDate) 比较两个日期进行订购。  
boolean equals​(Object obj) 比较两个日期来平等。  
static Date from​(Instant instant) 从 Instant对象获取 Date一个实例。  
long getTime​() 返回自1970年1月1日以来,由 Date对象表示的00:00:00 GMT的毫秒数。  
int hashCode​() 返回此对象的哈希码值。  

我们注重要掌握的是Calendar和DateFormat:

(1)Calendar:

推荐去看简书博主不姓马的小马哥关于Calendar的详解,写的非常详细:https://www.jianshu.com/p/6ef54da8932e

(2)DateFormat:

        SimpleDateFormat s1 = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("请输入你的生日:");
        String birth = new Scanner(System.in).nextLine();
        //parse将String类型转换为Date类型
        Date date = s1.parse(birth);
        long birthday = date.getTime();
        long current = System.currentTimeMillis();
        int age = (int) ((current-birthday)/1000/3600/24/365);
        System.out.println("你今年"+age+"岁");

7、BigDecimal:

使用BigDecimal(String val)来保证精度。

  Add(BigDecimal bd) : 做加法运算
  Subtract(BigDecimal bd) : 做减法运算
  Multiply(BigDecimal bd) : 做乘法运算
  Divide(BigDecimal bd) : 做除法运算,除不尽时会抛异常
  Divide(BigDecimal bd,保留位数,舍入方式) : 除不尽时使用
  setScale(保留位数,舍入方式) : 同上
  pow(int n) : 求数据的几次幂

8、枚举:

1、什么是枚举?
 枚举即穷举,指列出某些有穷序列集的所有成员的程序,要求成员的数量是有限的,可数的(不包括极限)。
 如:性别 : 男 女
 月份 :1-12月等。
 (1)当类的对象的数量是有限的,确定的,我们称此类为枚举类
 (2)当需要定义一组常量时,建议使用枚举类。
 (3)当枚举类中只有一个对象,则可以当作单例模式的一种实现方式。

2、如何定义枚举类:
 (1)自定义枚举类
(2)使用enum关键字(JDK5.0)

//自定义枚举类
class Season{
    //2、声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonSpec;

    //1、私有化类的构造器
    private Season(String seasonName, String seasonSpec){
        this.seasonName = seasonName;
        this.seasonSpec = seasonSpec;
    }
    //3、提供当前枚举类的多个对象 public static final
    public static final Season SPRING = new Season("spring","春暖花开");
    public static final Season SUMMER = new Season("summer","夏日炎炎");
    public static final Season AUTUMN = new Season("autumn","秋高气爽");
    public static final Season WINTER = new Season("winter","冰天雪地");

    //4、获取对象的属性
    public static Season getSPRING() {
        return SPRING;
    }

    public static Season getSUMMER() {
        return SUMMER;
    }

    public static Season getAUTUMN() {
        return AUTUMN;
    }

    public static Season getWINTER() {
        return WINTER;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonSpec='" + seasonSpec + '\'' +
                '}';
    }
}
enum Season1{
    //1、提供当前枚举类的多个对象 你就这么写 记得多个对象之间用,隔开,末尾对象有分号结束
     SPRING("spring","春暖花开"),
     SUMMER("summer","夏日炎炎"),
     AUTUMN("autumn","秋高气爽"),
     WINTER("winter","冰天雪地");


    //2、声明Season对象的属性:private final
    private final String seasonName;
    private final String seasonSpec;

    //1、私有化类的构造器
    private Season1(String seasonName, String seasonSpec){
        this.seasonName = seasonName;
        this.seasonSpec = seasonSpec;
    }
    //4、获取对象的属性
    public String getSeasonSpec() {
        return seasonSpec;
    }

    public String getSeasonName() {
        return seasonName;
    }
}

八、I/O:

1、I/O的分类:

按照流向,分为输入流和输出流。
按照操作单元,分为字节流和字符流。
按照流的角色,分为节点流和处理流。

Java中主要使用的I/O有三种:

(1)BIO:

BIO 属于同步阻塞 IO 模型 。同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到在内核把数据拷贝到用户空间。大大的浪费了CPU资源。

(2)AIO:异步IO模型,需要操作系统的支持。在IO多路复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在异步IO模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在IO完成后通知用户线程直接使用即可。

(3)NIO:阻塞非同步用户线程可以在发起IO请求后可以立即返回。但是CPU要一直轮询传输是否完成,占用了CPU资源。

2、File:

构造方法:

File​(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例。  
File​(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。  
File​(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。  
File​(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。  
 

方法:借用一下老师的图片

3、字符流

//字符流写出
    public static void Char_stream_Write() {
        File file = new File("D://test4.txt");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))){
            writer.write('1');
            writer.newLine();
            writer.write(98);
            writer.write("我是谁");
            writer.write("223");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

写入txt文件的内容是:

1
b我是谁223

        //字符流读入 
        File file = new File("D://test.txt");
        try (BufferedReader bufferedReader =new BufferedReader(new FileReader(file)))
        {
            String line = null;
            while ((line = bufferedReader.readLine())!= null){
                System.out.println(line);
            }
        }catch (Exception e){
            System.out.println("false");
        }

4、字节流

//字节流写出
    public static void Byte_stream_Write() {
        File file = new File("D://test5.txt");
        BufferedOutputStream writer = null;
        try {
            file.createNewFile();
            writer = new BufferedOutputStream(new FileOutputStream(file));
            writer.write('1');
            writer.write(98);
            writer.write(99);
            writer.write(100);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                writer.close();
            }catch (Exception e){
                System.out.println("false");
            }
        }


    }
public static void Byte_stream_Read() {
        char[] c1 = {'h','e','l','l','o'};
        File file = new File("D://test.txt");
        try ( BufferedReader breader = new BufferedReader(
                new InputStreamReader(
                        new BufferedInputStream(
                                new FileInputStream("D://test.txt")),"UTF-8")))
        {   CharArrayReader char1 = new CharArrayReader(c1);
            String line = null;
            System.out.println(char1.read());
            while ((line = breader.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

5、序列化和反序列化:

https://blog.csdn.net/weixin_43884234/article/details/114926995?spm=1001.2014.3001.5501

九、集合:

1、Collection:

被除了Map以外的所有集合类实现。

常用方法:

        boolean add(E e):添加元素。
        boolean addAll(Collection  c):把小集合添加到大集合中 。
        boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
        boolean isEmpty() :如果此 collection 没有元素,则返回 true。
        Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
        boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
        int size() :返回此 collection 中的元素数。
        Object[] toArray():返回对象数组。

2、List:

List接口继承Collection接口,包含Collection的所有方法。
 List接口的特点:
 1、具有索引
 2、有序的集合
 3、允许存储重复的元素

常见方法:

ListIterator<E> listIterator()
           返回此列表元素的列表迭代器(按适当顺序)。
  ListIterator<E> listIterator(int index)
           返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
  void add(int index, E element)
           在列表的指定位置插入指定元素(可选操作)。
  boolean addAll(int index, Collection<? extends E> c)
           将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
  List<E> subList(int fromIndex, int toIndex)
      返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
 E get(int index)
           返回列表中指定位置的元素。
 int indexOf(Object o)
           返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。

List接口有四个实现类:ArrayList,LinkedList,Vector,Stack;

 ArrayList类的特点:
 1、底层用数组定义,内存是连续的,查询快,修改慢。
 2、异步线程不安全。
 3、默认容量是10,每次默认扩容在原基础上扩展50%。
 4、构造方法可以初始化初始容量。

LinkedList类的特点:
 1、底层用双向链表定义,内存不连续,查询慢,修改快。
 2、异步线程不安全。

 Vector类的特点:与ArrayList类似
 1、数组定义。
 2、线程安全。
 3、默认容量是10,每次默认扩容在原基础上扩展100%。
 4、构造方法可以定义初试容量和容量增量。

 Stack类:
 是Vector的子类。遵循后进先出的原则
方法摘要
  boolean empty()
           测试堆栈是否为空。
  E peek()
           查看堆栈顶部的对象,但不从堆栈中移除它。
  E pop()
           移除堆栈顶部的对象,并作为此函数的值返回该对象。
  E push(E item)
           把项压入堆栈顶部。
  int search(Object o)
           返回对象在堆栈中的位置,以 1 为基数。

Arraylist 和 Vector 的区别?
ArrayList 是 List 的主要实现类,底层使用 Object[ ]存储,适用于频繁的查找工作,线程不安全 ;
Vector 是 List 的古老实现类,底层使用Object[ ] 存储,线程安全的。


Arraylist 与 LinkedList 区别?
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构
插入和删除是否受元素位置的影响:
ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置 i 插入和删除元素的话((add(int index, E element)) 时间复杂度近似为 O(n) ,因为需要先移动到指定位置再插入。
是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

3、Set:

Set接口:
 Collection的子接口,元素不可重复,用于给数据去重
 HashSet类:
 底层基于HashMap,Set接口的实现类,存储无序,唯一的对象,元素位置可能发生改变,线程不安全。

 LinkedHashSet类:
 有序,底层是哈希表和双向链表
 线程不安全

4、Queue:

Queue接口是Collection的子接口,LinkedList类是Queue接口的实现类。
 遵循先进先出原则

 方法摘要
  boolean add(E e)
           将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,
           如果当前没有可用的空间,则抛出 IllegalStateException。
  E element()
           获取,但是不移除此队列的头。
  boolean offer(E e)
           将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,
           此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。
  E peek()
           获取但不移除此队列的头;如果此队列为空,则返回 null。
  E poll()
           获取并移除此队列的头,如果此队列为空,则返回 null。
  E remove()
           获取并移除此队列的头。

5、Map:

Collection中的集合被称为单列集合,而Map中的集合被称为双列集合。每个元素由键(key)和值(value)两部分组成。key不可以重复,value可以重复,一个key只能对应一个value。

Map接口的常用实现类有HashMap,LinkedHashMap,Hashtable。

常用方法:

 void clear()
           从此映射中移除所有映射关系(可选操作)。
  boolean containsKey(Object key)
           如果此映射包含指定键的映射关系,则返回 true。
  boolean containsValue(Object value)
           如果此映射将一个或多个键映射到指定值,则返回 true。
  Set<Map.Entry<K,V>> entrySet()
           返回此映射中包含的映射关系的 Set 视图。
  boolean equals(Object o)
           比较指定的对象与此映射是否相等。
  V get(Object key)
           返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
  int hashCode()
           返回此映射的哈希码值。
  boolean isEmpty()
           如果此映射未包含键-值映射关系,则返回 true。
  Set<K> keySet()
           返回此映射中包含的键的 Set 视图。
  V put(K key, V value)
           将指定的值与此映射中的指定键关联(可选操作)。
  void putAll(Map<? extends K,? extends V> m)
           从指定映射中将所有映射关系复制到此映射中(可选操作)。
  V remove(Object key)
           如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
  int size()
           返回此映射中的键-值映射关系数。
  Collection<V> values()
           返回此映射中包含的值的 Collection 视图。

举个例子:

Map<Integer,String> map = new HashMap<>();
        map.put(23,"小明");
        map.put(24,"小芳");
        map.put(22,"小朱");
        //map.put(23,"小李"); //error 键值KEY不能重复
        map.put(25,"小龙");
        map.put(26,"小德");
        System.out.println(map);
        System.out.println(map.size());
        System.out.println("===================================");
        //map.clear(); //清空
        //根据键值删除
        map.remove(22);
        System.out.println(map);
        System.out.println("===================================");
        //判断映射是否包含某一个KEY
        System.out.println(map.containsKey(22));
        System.out.println(map.containsKey(23));
        System.out.println("===================================");
        //判断映射中是否包含某一个VALUE
        System.out.println(map.containsValue("小白"));
        System.out.println(map.containsValue("小德"));
        System.out.println("===================================");
        //返回对应KEY的VALUE
        System.out.println(map.get(23));
        System.out.println(map.get(24));
        System.out.println("===================================");
        //获取Map中所有键值对集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        Set<Map.Entry<Integer,String>> set3 = map.entrySet();
        System.out.println(set);
        System.out.println(set.getClass());
        System.out.println("===================================");
        //获取Map所有Key的集合
        Collection<Integer> set1 = map.keySet();
        System.out.println(set1);
        System.out.println(set1.getClass());
        System.out.println("===================================");
        //获取Map所有Value的集合
        Collection<String> c = map.values();
        System.out.println(c);

运行结果:

{22=小朱, 23=小明, 24=小芳, 25=小龙, 26=小德}
5
===================================
{23=小明, 24=小芳, 25=小龙, 26=小德}
===================================
false
true
===================================
false
true
===================================
小明
小芳
===================================
[23=小明, 24=小芳, 25=小龙, 26=小德]
class java.util.HashMap$EntrySet
===================================
[23, 24, 25, 26]
class java.util.HashMap$KeySet
===================================
[小明, 小芳, 小龙, 小德]
 

HashMap类:

HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过synchronized 修饰。因为线程安全的问题,HashMap要比HashTable效率高一点。另外,HashTable基本被淘汰,不要在代码中使用它。HashMap默认的初始化大小为16,加载因子是0.75。之后每次扩充,容量变为原来的2倍。创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小。JDK1.8以后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

总结一下HashMap的特点:

1、非线程安全。

2、效率比HashTable高。

3、初始大小为16,加载因子0.75,每次扩容变成原来的两倍。

4、底层用哈希表实现,元素的存取顺序不能保证一致。(注:LinkedHashMap采用的是哈希表+链表实现,保证元素的存取顺序一致。LinkedHasnMap的其他特点与HashMap一致。)

TreeMap:

TreeMap和HashMap都继承自AbstractMap,但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口。实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。实现SortMap接口让TreeMap有了对集合中的元素根据键排序的能力。默认是按key的升序排序,不过我们也可以指定排序的比较器。

6、Collection工具类:

1、排序

void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面


2、查找,替换操作

int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素

十、多线程:

1、进程和线程:

(1)进程:

是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。在Java中,当我们启动main函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。
(2)线程:

是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

2、并行和并发:

并发: 同一时间段,多个任务都在执行 (单位时间内不一定同时执行);
并行: 单位时间内,多个任务同时执行。

3、多线程的特点:

(1)可以提高CPU和I/O设备的利用率。

(2)多线程可以提高系统的并发能力。

(3)减少线程切换的开销。

(4)并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、死锁、线程不安全等等。

4、线程的状态:

5、死锁:

举个例子:

假设一个很窄的桥只够同时通过一个人,当两个人分别从桥的两边上桥,并且双方都不愿意先下桥。此时双方都无法通过此桥,我们称这就叫死锁。

6、死锁的条件:

互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

7、死锁的处理:

8、创建线程的方式:

方式一:

1、定义一个类继承Thread;

2、重写run方法;

3、创建子类对象;

4、调用start方法;

举个例子:

pubilic static void main(String[] args){   
    Person person = new Person();
    person.start();
}

class Person extends Thread{
    public void run(){
        for(int i =0;i<=20;i++){
            System.out.println("子线程:"+i);
        }
    }
}

方式二:(推荐)因为耦合性更低,且由于Java是单继承,这种写法更有利。

1、定义类实现Runnalbe接口。
2、覆盖接口的run方法。
3、创建Thread对象。
4、将Runnable的子类作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法。

举个例子:

public static void main(String[] args) {
        RunnableImpl runnable = new RunnableImpl();
        Thread thread = new Thread(runnable);
        thread.start();
        for(int i =20;i<=40;i++){
            System.out.println("主线程:"+i);
        }
    }

class RunnableImpl implements Runnable{
    @Override
    public void run() {
        for (int i = 40;i <= 60;i++){
            System.out.println("子进程2:"+i);
        }
    }
}

9、线程同步:

进程不安全:

如果一个进程在访问某的方法时,还没有进行值的运算和写回,此时另一个进程访问该方法,就会发生进程的安全问题。

线程同步是指在一个线程在对内存进行操作时,其他线程都不可以访问这个内存地址,直到该线程完成操作为止。实现进程同步有两种方法,同步代码块和同步方法;

synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

(1)同步代码块:

synchronized (o1){
            while (i<=100){
                System.out.println(name+"正在卖第"+ i++ +"号座位的票");
        }

(2)同步方法:

//同步普通方法
public synchronized void method(){

}
//静态同步方法
public static synchronized void method(){

}

十一、反射:

1、何为反射:

Java的反射是指程序在运行期可以拿到一个对象的所有信息。反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。

2、反射的作用:

class在JVM的运行过程中是多态加载的,以String类为例,当JVM加载String类时,它首先会读取String.class到内存,然后为String创建一个Class实例并关联起来。由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。

3、获取class实例的三种方法:

(1)通过.class的方法获取:

        Class<String> c = String.class;
        Class c1 = String.class;

(2)通过实例对象来获取:

        Person person = new Person();
        Object object = 123;
        Object double1 = 2.455;
        System.out.println(double1.getClass());
        System.out.println(object.getClass());
        System.out.println(person.getClass());

(3)通过字符串来获取:

        Class clz = Class.forName("java.util.Date");
        System.out.println(clz);

4、常用方法:

获取包名 类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名

获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)

获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)

获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)

反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法

反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法
举几个例子:

获取class:

   public static void main(String[] args) throws ClassNotFoundException {
        //获取class类型的三种方式
        //1、通过.class的方法获取
        Class<String> c = String.class;
        Class c1 = String.class;
        System.out.println(c+"\t"+c1);
        Class c2 = List.class;
        System.out.println(c2);
        Class c3 = int[].class;
        System.out.println(c3);
        Class c4 = double.class;
        System.out.println(c4);
        //2、通过对象获取
        Person person = new Person();
        Object object = 123;
        Object double1 = 2.455;
        System.out.println(double1.getClass());
        System.out.println(object.getClass());
        System.out.println(person.getClass());
        //3、通过字符串获取
        Class clz = Class.forName("java.util.Date");
        System.out.println(clz.getPackage().getName());
        System.out.println(clz.getName());
        System.out.println(clz.getSimpleName());
        System.out.println(clz);

    }
}
 class Person{
    private int age = 0;
     private String name = "";
     private double dou1 = 0;
    public Person(){ }
    public Person(int age,String name,double dou1){
        this.age = age;
        this.name = name;
        this.dou1 = dou1;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", dou1=" + dou1 +
                '}';
    }
}

获取构造方法:

 //获取构造方法
        Constructor constructor = c1.getConstructor();
        Constructor constructor1 = c1.getConstructor(int.class,String.class,double.class);
        //使用构造方法创建对象
        Person p2 =(Person) constructor.newInstance();
        Person p3 = (Person) constructor1.newInstance(23,"alice",24.5);
        System.out.println(p3.toString());

获取属性:

        Person alice = new Person(22,"alice",23.4);
        Class c = Person.class;
        Field[] f = c.getDeclaredFields();
        for (Field field : f) {
            field.setAccessible(true);
            System.out.println(field.getName());
        }

获取方法:

Class c = Person.class;
        Method[] m = c.getDeclaredMethods();
        for (Method method : m){
            method.setAccessible(true);
            System.out.println(method.getName());
        }
    }

暴力破解:

Class animal = Animal.class;
        Constructor constructor = animal.getDeclaredConstructor(int.class,String.class);

        //暴力破解 可以使用类的Private构造器
        constructor.setAccessible(true);
        Field age = animal.getDeclaredField("age");
        age.setAccessible(true);
        Animal animal1 = (Animal) constructor.newInstance(11,"dog");
        System.out.println(animal1);
        age.set(animal1,22);
        System.out.println(age.get(animal1));
        System.out.println(animal1);
        Method eat = animal.getDeclaredMethod("eat",String.class);
        eat.setAccessible(true);
        eat.invoke(animal1,"aq");

十二、网络编程:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值