Bootstrap

Java基础

文章目录

前言

本文分享了一些Java语法的基础, 有详有略, 供大家查阅

标识符

  • 字母,下划线_, 美元符号$开头
  • 字母,下划线,美元符号任意组合
  • 长度不限,大小写敏感
  • 不可以是关键字

标识符规范

  1. 表示类名:每个单词首字母大写
  2. 表示方法和变量:驼峰规则
  • java不采用ASCII字符集,而是Unicode编码

数据类型

变量

类型声明位置从属于生命周期(作用域)
局部变量方法或语句块内部方法/语句块从声明处开始,到方法或语句块结束
成员变量(实例变量)类内部,方法外部对象对象创建,成员变量也跟着创建。对象消失,成员变量也跟着消失;
静态变量(类变量)类内部,static修饰类被加载,静态变量就有效;

常量

final修饰,命名:全用大小写,单词之间用下划线

  • 字符常量 (1 , 2, 3 , true , false, "hello"等)
  • 符号常量(final修饰的)

long 类型的整形常量结尾加 L(大小写都可,一边用大写,易于区分)

long a = 3000000000L;(int类型一般为21亿左右,超过就要用long);

float f1 = 3.14F (浮点常量的类型默认为double,加F表示为float);

java中字符串不是基本类型,而是类

java中的boolean不能用0 1 代替,一般情况下,boolean占4字节和int一样,布尔类型数组时占一个字节


运算和运算符

  • 整数: 有long时,结果为long,没有long时,结果是int(包括short,byte)
  • 浮点数:有double时,结果为double,两个都是float时,结果才是float

java中char占两个字节,short 占两个字节,int 占4字节

字符串连接符:

如果“ + ”两侧的操作数有一个是String类型字符串,则会自动将另一侧的操作数转化为String,包括数字。

char类型相加 时,自动将结果转化为整型(根据ASCII计算);

char类型与整型运算是,结果也是整型

运算符优先级

  • 逻辑运算符:非 > 与 > 或

在这里插入图片描述

数据类型自动转化

容量小的(表示范围)可以自动转化为容量大的, int -> float, long -> float, byte -> short - > int;

特例,整型常量默认是 int, 但可以自动转化为 byte,short,char 只要不超过表示范围

数据类型强制转化


控制语句

switch

switch中表达式的值,是int(byte、short、char也可,long 不行)、枚举,字符串。

方法 Method

方法的重载 overload

只有形参列表不一样才构成重载

参数传值机制

所有的参数都是值传递(基本类型,引用类型)*重要面试点


面对对象(Object Oriented Programming)

field 表格 列

field 类 属性


属性(field)

方法(Method)

构造器(constructo)

构造器4个要点:

  • 构造器通过new关键字调用!!
  • 构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类), 不能在构造器里使用return返回某个值。
  • 如果我们没有定义构造器,则编译器会自动定义一个无参的道万法。如果已经定义则编译器不会自动添加!
  • 构造器的方法名必须和类名一致!

创建对象的四步:

  1. 分配对象空间,并将对象成员变量初始化为0或空
  2. 执行属性值的显式初始化
  3. 执行构造方法
  4. 返回对象的地址给相关的变量

每个源文件必须有且只有一个public 类, 并且与源文件保持一致

一个Java文件可以同时定义多个类

简单内存分析

在这里插入图片描述

Java虚拟机

栈 stack :存放方法

  1. 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
  2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
  3. 栈属于线程私有,不能实现线程间的共享!
  4. 栈的存储特性是“先进后出,后进先出”
  5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!

堆 heap : 存放对象

  1. 堆用于存储创建好的对象和数组(数组也是对象)
  2. JVM只有一个堆,被所有线程共享
  3. 堆是一个不连续的内存空间,分配灵活,速度慢!
  4. 堆被所有的线程所共享,在堆上的区域,会被垃圾回收器做进一步划分,例如新生代、老年代的划分。

在这里插入图片描述

方法区 Method area(也是堆): 存放类

  1. 方法区是JAVA虚拟机规范,可以有不同的实现。

    1. JDK7以前是“永久代”
    2. JDK7部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中
    3. JDK8是“元数据空间”和堆结合起来。
  2. JVM只有一个方法区,被所有线程共享!

  3. 方法区实际也是堆,只是用于存储类、常量相关的信息!

  4. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象,反射机制中会

  5. 重点讲授】、静态变量、字符串常量等)

  6. 常量池主要存放常量:如文本字符串、final常量值。

垃圾回收算法

1.引用计数算法

堆中的每个对象都对应一个引用计数器当有引用指向这个对象时,引用计数器加1,

而当指向该对象的引用失效时(引用变为null),引用计数器减1,

最后如果该对象的引用计算器的值为0时,则Java垃圾回收器会认为该对象是无用对象并对其进行回收。

优点是算法简单,缺点是“循环引用的无用对象”无法别识别。

在这里插入图片描述

2.引用可达法(根搜索算法)

程序把所有的引用关系看作一张图

从一个节点GC ROO开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,

当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

3.通用的分代垃圾回收机制

在这里插入图片描述

内存泄漏

指堆内存由于某种原因程序未释放,造成内存浪费,导致运行速度减慢甚至系统崩溃等。

  1. 创建大量无用对象

比如:大量拼接字符串时,使用了String而不是StringBuilder。

  1. 静态集合类的使用

像HashMap、Vector、List等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象也不能被释放。

  1. 各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭

IO流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。

  1. 监听器的使用不当

释放对象时,没有删除相应的监听器

其他要点

1.程序员无权调用垃圾回收器。

2.程序员可以调用System.gc(),该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full GC,成本高,影响系统性能。

\3. Object对象的finalize方法,是Java提供给程序员用来释放对象或资源的方法,但是尽量少用


几个重要的关键字

this 的本质和用法

  • 普通方法中,this总是指向调用该方法的对象。
  • 构造方法中,this总是指向正要初始化的对象。
  • this()调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
  • this 不能用于static方法中。

static 关键字

static方法和属性都属于类

静态变量/静态方法生命周期和类相同,在整个程序执行期间都有效。它有如下特点:

  • 为该类的公用变量,属于类,被该类的所有实例共享,在类载入时被初始化。
  • static 成员变量只有一份。
  • 一般用“类名.类属性/方法”来调用。
  • 在static方法中不可直接访问非static的成员。

静态初始化块

  • 构造方法用于对象的普通属性初始化。
  • 静态初始化块,用于类的初始化操作,初始化静态属性。
  • 在静态初始化块中不能直接访问非static成员。
  • 静态初始化块在类加载的时候就会被执行。

注意事项

静态初始化块执行顺序(学完继承再看这里):

上溯到Object类,先执行Object 的静态初始化块,再向下执行子类的静态初始化块,直到当前类的静态初始化块为止。

构造方法执行顺序和上面顺序一样!!!

包机制(package, import)

package的使用有两个要点:

  1. 通常是类的第一句非注释性语句, 说明该类所在的包
  2. 包名:域名倒着写即可,再加上模块名,便于内部管理类。
  3. 同一个包下类名不能相同。
  4. 调用不同包下的类,需要导入(import)该类,或者该类所在的整个包。
  5. 不导入时需要写完整的报名+类名
  6. 调用不同包下同名的类时,只能写完整的包名 + 类名
JDK中的主要包
java中的常用包说明
java.lang(不需要导入)包含一些Java语言的核心类,如String、Math、Integer、System和Thread。
java.awt包含了构成抽象窗口工具集 (abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
java.net包含执行与网络相关的操作的类。
java.io包含能提供多种输入/输出功能的类。
java.util包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

导入类(import)(包)

注意要点

  • Java 会默认导入java.lang 包下所有的类,因此这些类我们可以直接使用。
  • 如果导入两个同名的类,只能用包名+类名来显示调用相关类: java.util.Date date = new java.util.Date();
// 表示导入java.util包下的所有类,会降低编译速度,但不会降低运行速度 
// 实际编译时依然会根据使用的类来编译,并不会编译所有. 
import java.util.* 

静态导入(类)

  • 静态导入(static import):

其作用是用于导入指定的静态属性和静态方法,这样我们可以直接使用静态属性和静态方法。

// 示例 
import java.lang.Math.*; 
import java.lang.Math.PI;

继承 (extends)

面对对象的三大特征:

继承, 封装, 多态

定义类时如果没写继承, 会默认继承 Object 类, 即所有的类都会继承Object类

判断继承关系

instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。比如:

继承使用要点

  1. 父类也称作超类、基类。
  2. 子类:派生类等。
  3. Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
  4. Java中类没有多继承,接口有多继承。
  5. 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
  6. 如果定义一个类时,没有调用extends,则它的父类是: java.lang.Object。

方法重写 (override)

子类重写父类的方法,可以用自身行为替换父类行为。重写是实现多态的必要条件。方法重写需要符合下面的三个要点:

  1. “= =”:方法名、形参列表相同。
  2. “≤”︰返回值类型和声明异常类型,子类小于等于父类。(返回值类型相同, 或者是子类);
  3. “>”:访问权限,子类大于等于父类。

final关键字

final关键字的作用:

  • 修饰变量:被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。

final int MAX_SPEED = 120;

  • 修饰方法:该方法不可被子类重写。但是可以被重载!

final void study(){}

  • 修饰类:修饰的类不能被继承。比如: Math、String等。

final class A {}

组合(component)

  • 将父类的对象作为子类的属性

在子类(概念上的)中创建一个父类的对象,这样就可以使用父类的属性和方法了。

组合比较灵活,继承只有一个父类,但是组合可以有多个类的属性。

具体使用时应考虑类与类之间的逻辑关系。

Object类

所有的类都是Object类的子类

toString() 方法

默认返回完整的类名+对象的地址。

在下列代码中,默认调用 toString 方法。

public class TestObject extends Object{
    public static void main(String[] args) {
        TestObject t1 = new TestObject();
        System.out.println(t1);
    }
}

equals() 方法

equals() 和 “==” :

“==” 用于比较双方是否相等,基本类型比较值,引用类型比较地址

equals() 用于比较对象是否相等,默认是比较对象的hashcode,可以重写为比较相同对象的特定属性。

super关键字

  1. super“可以看做”是直接父类对象的引用。可通过super来访问父类中被子类覆盖的方法或属性。
  2. 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
  3. 在一个类中,若是构造方法的第一行没有调用super(…)或者this(…); 那么Java默认都会调用super(),含义是调用父类的无参数构造方法。
  4. super 只能在子类内部调用

在这里插入图片描述

继承树追溯

  • 属性/方法查找顺序:(比如:查找变量h)

    • 查找当前类中有没有属性h
    • 依次上溯每个父类,查看每个父类中是否有h,直到Object如果没找到,则出现编译错误。
    • 上面步骤,只要找到h变量,则这个过程终止。
  • ·构造方法调用顺序:

    • 构造方法第一句总是: super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。

封装 (encapsulation)

封装的理念: 高内聚,低耦合

访问权限修饰符

修饰符同一个类同一个包子类所有类
private(私有)
default(缺省, 不写)
protected(受保护的)
public(公有)

【注】关于protected的两个细节:

  1. 若父类和子类在同一个包中,子类可访问父类的 protected成员,也可访问父类对象的protected成员。
  2. 若子类和父类不在同一个包中,子类可访问父类的protected成员,不能访问父类对象的protected成员。
  3. 在同一个包中,没有继承关系,可以访问父类对象的protected成员。

类成员访问权限控制

public全局友好
protected父子友好,包内友好
default包内友好
private类内友好

开发中封装的简单规则:

  • 属性─般使用private访问权限。

    • 属性私有后,提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是 is开头!)。
  • 方法:一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。

多态 (polymorphism)

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。

多态的要点:

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)。
  2. 多态的存在要有3个必要条件: 继承,方法重写,父类引用指向子类对象。
  3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

父类引用可以访问子类重写的方法,但是不能访问子类独有的方法。

此时,父类是编译类型,子类是运行时类型。

对象的转型(casting)

  1. 父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
  2. 向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型

一个父类有很多子类时,当父类的引用指向子类的对象时,可以向下转型为该子类的对象,如果转型为其他子类的对象,就会出错。

// 类型转换(伪代码)
class Animal {}
class Dogs extends Animal{}
class Cat extends Animal{}
public viod main() {
    // 父类引用指向子类对象(多态)
    Animal animal = new Dogs(); // 向上转型,此时可以调用子类继承的方法,
                                // 但不能调用子类独有的方法
    Dog dog = (Dog)animal;      // 向下转型,可以调用子类独有的方法。
    Cat cat = (Cat)animal;      // 类型转化错误:ClassCastException
}

抽象类

抽象方法

  • 使用abstract修饰的方法,没有方法体,只有声明。
  • 定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

抽象类

  • 包含抽象方法的类就是抽象类。
  • 通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

抽象类的使用要点:

  1. 有抽象方法的类只能定义成抽象类
  2. 抽象类不能实例化,即不能用new来实例化抽象类。
  3. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new 实例,只能用来被子类调用。
  4. 抽象类只能用来被继承。
  5. 抽象方法必须被子类实现。

接口(interface)

接口 interface, 实现 implements

  1. 接口全面的专业的实现了: 规范和具体实现的分离。
  2. 接口和实现类不是父子关系, 是实现规则关系.
  3. 接口也是类, 一种特殊的类

定义和使用接口

声明格式:

[访问修饰符] interface 接口名 [extends 父接口1, 父接口2] {
    常量定义;
    方法定义;
}

定义接口的详细说明:

  • 访问修饰符: 只能是public或默认。
  • 接口名: 和类名采用相同命名机制。
  • extends: 接口可以多继承。
  • 常量: 接口中的属性只能是常量,总是: public static final修饰。不写也是。
  • 方法: 接口中的方法只能是: public abstract。省略的话,也是public abstract.

要点

  • 子类通过implements来实现接口中的规范。接口不能创建实例,但是可用于声明引用变量类型。
  • 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
  • JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
  • JDK1.8(含8)后,接口中包含普通的静态方法、默认方法。

接口默认方法(扩展方法)

默认方法用default修饰

接口可以提供默认方法的实现,所以实现类都可以得到默认方法,默认方法可以被重写,也可以不重写

接口静态方法

接口中可以直接定义静态方法, 这种静态方法从属于接口, 可以直接使用接口名来调用. 不可以通过子类调用

子类中如果定义了相同名字的静态方法, 那就是完全不同的方法.

静态方法不能调用普通方法, 普通方法属于对象,

普通方法可以调用静态方法

接口的多继承(multiple inheritance)

接口可以多继承

interface A {
    void testA();
}
interface B {
    void testB();
}
// 接口的多继承
interface C extends A, B{
    void testC();
}
// 实现接口和父级接口的抽象方法.
class CImpl implements C {
    void testA() {
            
    }
    void testB() {
            
    }
    void testC() {
            
    }
}

子类实现接口时必须实现接口和父级接口的抽象方法。

内部类 (inner class)

我们把一个类放在另一个类的内部定义,称为内部类(inner class).

内部类分类成员内部类非静态内部类
静态内部类
匿名内部类只使用一次的类
局部内部类定义在方法内部, 仅作用于该方法

内部类的两个要点

  • 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
  • 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。但外部类不能访问内部类的内部属性。

注意

内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为 Outer的外部类和其内部定义的名为lnner的内部类。编译完成后会出现 Outer.class和 Outersinner.class 两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。

非静态内部类

(外部类里使用非静态内部类和平时使用其他类没什么不同)

  1. 1.非静态内部类对象必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。

  2. 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。

  3. 非静态内部类不能有静态方法、静态属性和静态初始化块

  4. 成员变量访问要点:

    1. 内部类属性: this.变量名
    2. 外部类属性:外部类名.this.变量名

内部类的访问

1.外部类中定义内部类: new Inner0。

2.外部类以外的地方使用非静态内部类

Outer.nner varname = new Outer().new Inner().

内部类对象依存于外部类对象, 现有外部类对象, 再有内部类对象,

静态内部类

定义方式

static class ClassName {
    // 类体
}

使用要点:

  • 静态内部类可以访问外部类的静态成员, 不能访问外部类的普通成员;
  • 静态内部类可以看作外部类的一个静态成员;

匿名内部类(anonymous inner class)

只使用一次的类, 开发中常见.

// 语法
new 父类构造器(实参列表)\ 实现接口 () {
    // 类体
}
// 匿名内部类的使用
public class TestAnonymousInnerClass {
    public void test1 (A a) {
        a.run();
    }

    public static void main(String[] args) {
        TestAnonymousInnerClass t = new TestAnonymousInnerClass();
        t.test1(new A() {
            @Override
            public void run() {
                System.out.println("TestAnonymousInnerClass.run");
            }
        });
    }
}
interface A {
    void run();
}

局部内部类(local inner class )

定义在方法内部的, 作用域仅限于本方法, 称为局部内部类.

在开发中应用很少.

// 局部内部类的使用
public class TestLocalInnerClass {
    public void show() {
        class Inner3 {
            public void fun() {
                System.out.println("hello world");
            }
        }
        new Inner3().fun();
    }

    public static void main(String[] args) {
        new TestLocalInnerClass().show();
    }
}

面向对象大总结

在这里插入图片描述

数组

数组也是对象

数组的概念

数组的定义

数组是相同数据类型的有序集合。

数组的四个特点

  1. 数组的长度是确定的。数组一旦被创建, 大小就是不可变的。但是数组的引用可以改变。
  2. 元素类型必须相同
  3. 数组类型可以是基本类型和引用类型
  4. 数组变量是引用类型(可变)。数组也是对象, 数组的元素相等与对象的属性。

数组的初始化

  1. 静态初始化
public class Test {
    public static void main (String Args[]) {
        int[] a = new int[10];
        a[0] = 1;
        a[1] = 2;
        ClassDemo[] c= new ClassDemo[10];
        c[0] = new ClassDemo();
        c[1] = new ClassDemo();
    }
}
  1. 动态初始化
public class Test {
    public static void main (String Args[]) {
        int[] a = new int[10];
        a[0] = 1;
        a[1] = 2;
        ClassDemo[] c= new ClassDemo[10];
        c[0] = new ClassDemo();
        c[1] = new ClassDemo();
    }
}
  1. 默认初始化

数组是对象, 它的元素相当于对象的属性, 每个元素也按照属性的方式被默认初始化。

for-each 循环

专门用于读取数组或容器中的所有元素。

public static void main(String[] args) {
    String [] ss = {"aa", "bb", "cc"};
    for (String temp:ss) {
        System.out.println(temp);    
    }
}
  1. for - each 增强for循环在遍历时只能读取, 不能修改.
  2. for - each 增强for循环, 不涉及有关索引的操作。

数组的拷贝

方法**: System.arraycopy(object src, int srcpos, object dest, int destpos, int lenght)**

src : 源数组

srcpos : 源数组中的起始位置

dest : 目标数组的起始位置.

length : 要复制的长度

java.util.Arrays类

包含排序、查找、填充、打印内容等常见的数组操作。

import java.util.Arrays;

public class Test01 {
    public static void main(String[] args) {
        // 静态初始化
        int[] a = {1, 2, 232, 23, 543, 12, 59};
        // 排序
        Arrays.sort(a);
        // 二分法查找, 数组必须是有序的
        Arrays.binarySearch(a, 12);
        // 填充数组
        Arrays.fill(a, 2, 4, 100);
        // 输出打印数组
        System.out.println(Arrays.toString(a));
    }
}
/*
输出结果 : 
[1, 2, 100, 100, 59, 232, 543]
*/

多维数组

Java中多维数组的声明和初始化应按从低维到高维(从左到右)的顺序进行。

低维的长度没有定义, 高维的长度就无法确定。

int[][] a = new int[3][];    // 正确
int[][] a1 = new int[][4];   // 错误

int[][] b = {                // 正确
        {1, 3, 5, 7},
        {4, 6, 8, 10, 12},
        {2, 5, 8, 11, 14}
};

int[][] c = new int [3][];
//c[0] = {1, 3}; // 错误, 没有声明类型就初始化.
c[0] = new int[] {1, 3}; // 正确

Object 数组存储表格

1001张三27java工程师
1002李四28网络爬虫工程师
1003王五32算法工程师
// Object 数组存储表格数据
Object[][] example = new Object[3][];
example[0] = new Object[] {1001, "张三", 27, "java工程师"};
example[1] = new Object[] {1002, "李四", 28, "网络爬虫工程师"};
example[2] = new Object[] {1003, "王五", 32, "算法工程师"};
for (Object[] each:example) {
    System.out.println(Arrays.toString(each));
}

Object 数组的元素是对象, 整型 int 本质上是基础数据类型, 不是对象, 但是Java会自动装箱(包装类).

javabean 加数组 存储表格

javabean的三个要点

  • 这个Java类必须具有一个无参的构造函数
  • 属性必须私有化。
  • 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
// 测试 javabean 加数组 存储表格
public class Test02 {
    public static void main(String[] args) {
        Emp[] emps = {
                new Emp(1001, "张三", 27, "Java工程师"),
                new Emp(1002, "李四", 28, "网络爬虫工程师"),
                new Emp(1003, "王五", 32, "算法工程师")
        };
        for (Emp each :
                emps) {
            System.out.println(each.toString());
        }
    }
}
class Emp {
    private int id;
    private String name;
    private int age;
    private String job;

    public Emp() {
    }

    public Emp(int id, String name, int age, String job) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.job = job;
    }

    @Override
    public String toString() {
        return "[" + id +
                ", " + name +
                ", " + age +
                ", " + job +
                ']';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }
}

Comparable接口

  • 用于定义比较的策略

  • Java中排序算法的底层也会依赖Comparable接口

  • Comparable接口只有一个方法:

    • public int compareTo(Object obj)
    • obj 为要比较的对象
  • 将当前对象和obj进行比较,

    • 如果:

      • 大于返回 1
      • 小于返回 -1
      • 相等返回 0

异常机制 (Exception)

异常机制的核心类

在这里插入图片描述

Java是采用面向对象的方式来处理异常的.

处理过程:

  • 抛出异常: 在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一

对象,停止当前执行路径,并把异常对象提交给JRE。

  • 捕获异常: JRE 得到该异常后,寻找相应的代码来处理该异常。JRE 在方法的调用

栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止

异常的分类:

  • Error是程序无法处理的错误

  • Exception是升序本身可以处理的异常

    • RuntimeException (运行时异常) 可以通过编译。不需要try-catch

      • NullPointerException
      • ArithmeticException
      • NumberFormatException
    • CheckedException (已检查异常) 不能通过编译。需要try-catch

      • SQLException
      • ClassNotFoundException

异常的处理方式之一:捕获异常

使用 try-catch-finally 语句, 捕获异常

try {
    // 语句 1;
    // 语句 2;
    // 语句 3;
} catch (Exception1 e) {        // 如果有多类异常, 父类要放在子类上面
    e.printStackTrace();
}catch (Exception2 e) {
    e.printStackTrace();
}catch (Exception3 e) {
    e.printStackTrace();
} finally {        // 无论发生异常与否, 都会执行的语句
    // 语句 4;     // 可用于关闭资源.
    // 语句 5;
}

实际应用

public static void main(String[] args) {
    FileReader reader = null;
    try {
        reader = new FileReader("d:/a1.txt");
        char c0 = (char)reader.read();
    } catch (IOException e) {
        e.printStackTrace();
    }
    finally {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

try :

  • try 语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任

意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或

几种类型的异常对象,它后面的 catch 语句要分别对这些异常做相应的处理。

  • 一个 try 语句必须带有至少一个 catch 语句块或一个 finally 语句块。

当异常处理的代码执行结束后, 不会回到 try 语句去执行尚未执行的代码。

catch:

  • 每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类

型的异常对象。

  • catch 捕获异常时的捕获顺序

    • 如果异常类之间有继承关系,先捕获子类异常再捕获父类异常

finally:

  • 不管是否发生异常,都必须执行。
  • 通常在finally中关闭已经打开的资源,比如:关闭文件流、释放数据库连接等。

异常处理方式之二:声明异常

  • CheckedException产生时,不一定立刻处理它,可以把异常 throws 抛出,由调用者处理。
  • 一个方法抛出多个CheckedException时, 就必须在声明处列出所有的异常。
  • 如果子类异常和父类异常同时存在时, 只写父类即可。
public static void main(String[] args) throws IOException {
    FileReader reader = null;
    try {
        reader = new FileReader("d:/a.txt");
        char c0 = (char)reader.read();
    } finally {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resource

  • try-with-resource 自动关闭 Closable 接口的资源
  • JDK7 之后,新增了“try-with-resource"它可以自动关闭实现了AutoClosable 接口的类,实现类需要实现 close0方法。”try-with-resources声明”,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编译时仍然会进行转化为 try-catch-finally 语句。
public static void main(String[] args) {
    try(FileReader reader = new FileReader("d:/a.txt")) {
        char c = (char)reader.read();
    }catch (Exception e) {
        e.printStackTrace();
    }
}

自定义异常

  • 在程序中,可能会遇到JDK 提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。
  • 自定义异常类只需从 Exception 类或者它的子类派生一个子类即可。
  • 自定义异常类如果继承 Exception 类,则为 CheckedException 异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException 类
  • 习惯上,自定义异常类应该包含 2 个构造器:一个是默认的构造器,另一个是带有详细信息的构造器

包装类(Wrapper Class)

包装类基本知识

Java 是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到 Objectl ]数组或集合中的操作等等。

为了解决这个不足,Java 在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。

包装类位于java.lang 包,八种包装类和基本数据类型的对应关系

基本数据类型对应包装类
基本数据类型包装类
byteByte
booleanBoolean
shortShort
charCharacter
intInteger
longLong
floatFloat
doubleDouble

Number类

八中数据类型中有六种是数字类型,这六种数字类型的基本类型的包装类都是Number类的子类。

Number类是抽象类,因此它的抽象方法,所有的子类都要实现。Number类提供了供所有子类进行类型转换的方法。

在这里插入图片描述

public static void main(String[] args) {
    Integer i = new Integer(10);       // 不推荐使用
    Integer j = Integer.valueOf(20);        // 官方推荐
    // 把包装类转成基本数据类型
    int a = j;              // 可以, 但是不推荐
    int b = j.intValue();   // 正确
    // 把字符串转成数字
    Integer m = Integer.valueOf("456"); // 正确
    int n = Integer.valueOf("456");
    // valueof 方法返回的是Integer对象
    // parseInt 方法返回的是int整型
    // 但是int 和 Integer 之间会自动转换, 所有混用也没关系, 但是不推荐.
    int s = Integer.parseInt("123");    // 正确
    Integer t = Integer.parseInt("123");
}

自动装箱和拆箱

将基本数据类型和包装类自动转换

Integer缓存问题 (IntegerCache)

IntegerCache类是Integer类的一个内部类, 该类中存在一个 Integer[] cache 数组, 存放"-128 ~ 127"的Integer对象, 以提高效率.

在创建value范围在 “-128 ~ 127” 的 Interger 对象时, 会直接返回cache数组中对应的对象.

即, value在该范围内的Integer对象, 如果value相等, 则是同一个对象.

Java常见类

String类

String类详解

  1. String 类又称作不可变字符序列。
  2. String 位于java.lang 包中,Java 程序默认导入java.lang 包下的所有类
  3. Java 字符串就是 Unicode 字符序列,例如字符串“Java”就是4个 Unicode 字符组成的。
  4. Java 没有内置的字符串类型,而是在标准Java 类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是 String 类的一个实例。
// String 的初始化
String s0 = null; //字符串为空
String s1 = "";//空字符串, 两者含义不同.
String s2 = "java";
String s3 = new String("java");

常量池原理

String g1 = "北京尚学堂";
String g2 = "北京尚学堂";
String g3 = new String("北京尚学堂");

在这里插入图片描述

String 类的常用方法

方法用途
char charAt(int index)返回字符串中下标为 index 的字符
boolean equals(String other)判断字符串是否相等; 相等返回true, 否则返回false
boolean equalsIgnoreCase(String other)判断是否相等(忽略大小写)
int indexOf(String str)返回第一个字符串str的下标, 否则返回-1
int lastIndexOf(String str)返回最后一个字符串str的下标, 否则返回-1
int length()返回字符串长度
String replace(char oldChar, char new)返回一个新串, 以newChar替换原字符串中的所有oldChar,
String replaceAll(String regex, String replacement)返回一个新串, 以 replacement 替换原字符串中所有满足表达式regex的子串
boolean startsWith(String prefix)判断字符串是否以prefix开始, 是返回true, 否则返回false
boolean endsWith(String prefix)判断字符串是否以prefix结束, 是返回true, 否则返回false
String subString(int beginIndex)返回一个新串, 包含原字符串从下标为beginIndex到末尾
String subString(int beginIndex, int endIndex)返回一个新串, 包含原字符串从下标为beginIndex(包含)到下标为endIndex(不包含)的子串
String toLowerCase()返回一个新串, 将原串所有字符小写
String toUpperCase()返回一个新串, 将原串所有字符大写
String trim()返回一个新串, 去除原串头尾的所有空格

str1 == str2 是判断两个字符串的对象是否相同. str1.equals(str2)判断字符串本身是否一致.

StringBuffer类 和 StringBuilder类

  • StringBuilder 线程不安全, 不做线程检查, 效率较高, 常用
  • StringBuffer 线程安全, 做线程同步检查, 效率较低

StringBuilder 和StringBuffer 的方法大多都是修改原对象, 而后返回原对象, 因此可以写方法链;

时间相关类

在计算机的世界里, 我们把1970年1月1日00:00:00 定为基准时间, 每个度量单位毫秒.

在这里插入图片描述

Date时间类(java.util.Date)

DateFormat类 和 SimpleDateFormat类

DataFormat类是一个抽象类, SimpleDateFormat是子类.

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateFormat {
    public static void main(String[] args) throws ParseException {
        // 将字符串按特定模式转换成Data对象
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String str = "2049-10-1 10:10:10";
        Date guoqing = format.parse(str);
        System.out.println(guoqing.getTime());
        System.out.println(guoqing);

        // 将Date对象转换为字符串, 按特定模式
        DateFormat format2 = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒");
        Date date2 = new Date();
        String date2str = format2.format(date2);
        System.out.println(date2str);
    }
}

在这里插入图片描述

在这里插入图片描述

Calendar 日历类

Calendar类是一个抽象类, 提供了关于日期的展示和计算功能.

GregorianCalendar 是Calendar的子类, 表示公历.

💡值得注意的是, 该类中, 月份的表示从 0 开始, 即 January对应的是0, February对应的是1;

星期的表示为, Sunday是一周里的第一天, 对应 1 , Saturday 对应 7;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class TestCalendar {
    public static void main(String[] args) {
        // 得到相关日期元素
        GregorianCalendar calendar = new GregorianCalendar
                (2049, 9, 1, 22, 10, 50);

        calendar.setTime(new Date());
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
        int date = calendar.get(Calendar.DATE);
        int day = calendar.get(Calendar.DAY_OF_WEEK) - 1;
        System.out.println("====================");
        System.out.println(year);
        System.out.println(month);
        System.out.println(day_of_month);
        System.out.println(date);
        System.out.println(day);
        System.out.println("====================");
        System.out.println(calendar);
        System.out.println(getCalendar(calendar));
        // 设置日期
        calendar.set(Calendar.YEAR, 2099);
        calendar.set(Calendar.MONTH, Calendar.MARCH);
        calendar.set(Calendar.DAY_OF_WEEK, 10);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 10);
        calendar.set(Calendar.MINUTE, 10);
        calendar.set(Calendar.SECOND, 10);
        System.out.println(getCalendar(calendar));
        // 日期计算
        calendar.add(Calendar.MONTH, 10);
        calendar.add(Calendar.DATE, 11);
        System.out.println(getCalendar(calendar));
    }
    static String getCalendar (Calendar calendar) {
        return "" +
                calendar.get(Calendar.YEAR) + "年" +
                (calendar.get(Calendar.MONTH)+1) + "月" +
                calendar.get(Calendar.DAY_OF_MONTH) + "日, 星期" +
                (calendar.get(Calendar.DAY_OF_WEEK)-1)  + " " +
                calendar.get(Calendar.HOUR) + ":" +
                calendar.get(Calendar.MINUTE) + ":" +
                calendar.get(Calendar.SECOND);
    }
}

设置日期时, 当设置的date和day_of_week冲突时, 会选择后写的那个, 并且自动计算出前者,

创建Calendar对象时, 不传参会自动使用当前系统时间.

数学相关类(Math, Random)

File 类

可以访问文件, 硬盘系统

File类的基本用法

java.io.file类: 代表文件和目录, 用于: 读取文件, 创建文件, 删除文件, 修改文件

File类常见构造方法: public File(String pathname)

以pathname为路径创建File对象, 如果pathname是相对路径, 则 默认的当前路径在系统属性 “user.dir” (当前项目所在的路径)中存储.

创建File对象, 创建文件

File类创建文件夹或目录的方法

  • createNewFile() 创建新的File
  • delete() 删除File对应的文件
  • mkdir() 创建一个目录, 中间某个目录缺失, 则创建失败
  • mkdirs() 创建 多层 目录, 中间某个目录缺失, 则创建失败
import java.io.File;
import java.io.IOException;

public class TestFile1 {
    public static void main(String[] args) throws IOException {
        System.out.println(System.getProperty("user.dir"));
        File f = new File("a.txt"); // 相对路径, 默认放到 user.dir目录下, 即项目所在目录
        f.createNewFile(); // 创建文件
        File f2 = new File("d:/b.txt"); // 绝对路径
        f2.createNewFile();
    }
}
File类访问属性的方法
方法说明
public boolean exists()判断File是否存在
public boolean isDirectory()判断File是否是目录(文件夹)
public boolean isFile()判断File是是否是文件
public long lastModified返回File最后修改的时间
public long length()返回File大小
public String getName()返回文件名
public String getPath()返回文件的目录路径

枚举类型

枚举类型的定义包括枚举声明和枚举体, 格式如下

enum 枚举名 {
    枚举体(商量列表);
}

所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!

而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的。

可以直接通过枚举类型名使用它们。

  • 当你需要定义一组常量时, 可以使用枚举类型
  • 尽量不要使用枚举的高级特性, 事实上, 高级特性都可以使用普通类来实现, 没有必要增加复杂性
public class TestEnum {
    public static void main(String[] args) {
        // 遍历枚举类型
        for (Season s :
                Season.values()) {
            System.out.println(s);
        }
        // 枚举类型结合switch语句
        int a = new Random().nextInt(4);
        Season s = Season.values()[a];
        switch (s) {
            case SPRING:
                System.out.println("春天");
                break;
            case SUMMER:
                System.out.println("夏天");
                break;
            case AUTUMN:
                System.out.println("秋天");
                break;
            case WINTER:
                System.out.println("冬天");
                break;
        }
    }
}

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

容器和泛型

泛型(generic)

泛型的定义

泛型是JDK1.5 以后增加的,它可以帮助我们建立类型安全的集合。

泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参

数传入。 我们可以把“泛型”理解为数据类型的一个占位符(类似: 形式参数),即告诉编

译器,在调用泛型时必须传入实际类型。这种参数类型可以用在类、接口和方法中,分别被

称为泛型类泛型接口泛型方法

  • 把类型当做参数一样传递
  • <数据类型>只能是引用类型
类型擦除

编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。

泛型是一个编译的概念,编译后生成的字节码 class 文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成 obiect,运行时虚拟机并不知道泛型。

使用泛型的好处
  • 代码可读性更好, 书写更方便
  • 程序更安全
定义泛型

定义泛型的字符可以是任何标识符, 一般采用: E, T, K, V, N, ?

EElement在容器中使用, 表示容器中的元素
TType表示普通的Java类
KKey表示键, 例如Map中的Key
VValue表示值
NNumber表示数字类型
?表示不确定的类型

泛型类

public class Test { public void main(String args[]) { Mygeneric mygeneric = new MyGeneric<>(); } } class Mygeneric { }

泛型类在声明时要在类的旁边, 使用<>声明泛型, 在使用泛型类创建实例对象时, 要给出实际的类型.

泛型接口

使用泛型的接口在声明时和泛型类相同,

在实现类实现接口时, 可以有两种方法,

可以直接明确泛型的类型

也可以先不明确给定类型, 等该类在实例化的时候在确定.

在引用泛型接口的实现类实例化时, 可以使用实现类名来声明对象的类型, 也可以直接使用接口名来声明.

如果实现类在定义时已经给定了泛型的具体类型,那么, 在使用类名声明对象类型时, 就不用考虑泛型了.(因为泛型的类型已经确定), 但是在使用接口名声明对象类型时, 还是要给出泛型的类型(实现类定义的类型).

总之, 在创建对象时, 声明部分有泛型, 就要给定具体的类型.

public class TestMyGeneric {
    public static void main(String[] args) {
        // 在创建对象时, 如果该类已经给出具体类型, 就不需要考虑泛型了
        MyGenericImpl01 myGeneric1 = new MyGenericImpl01();
        myGeneric1.getName("贾国荣");
        // 使用接口名声明对象时, 必须给出类型
        MyGeneric<String> myGeneric2 = new MyGenericImpl01();
        myGeneric2.getName("贾国荣");
        // 该类没有给出泛型的具体类型时, 要在创建类时给出
        MyGenericImpl02<String> myGeneric = new MyGenericImpl02<>();
        myGeneric.getName("贾国荣");

    }
}

interface MyGeneric <T> {
    T getName (T name);
}

// 在实现类的声明时就确定泛型的具体类型
class MyGenericImpl01 implements MyGeneric<String> {

    @Override
    public String getName(String name) {
        return null;
    }
}
// 没有确定具体类型
class MyGenericImpl02<T> implements MyGeneric<T> {


    @Override
    public T getName(T name) {
        return null;
    }
}

泛型方法

泛型类中所定义的泛型,在方法中也可以使用。当方法不实用泛型, 而是需要仅仅在某一个方法

上使用泛型时,这时候可以使用泛型方法。

泛型方法是指将方法的参数类型定义成泛型以便在调用时接收不同类型的参数。类型参数可以有多个,用逗号隔开,如:。定义时,类型参数一般放到返回值前面。

调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型来。

非静态泛型方法
public class TestMethodGeneric {
    public static void main(String[] args) {
        MethodGeneric methodGeneric = new MethodGeneric();
        methodGeneric.getName("贾国荣");
        
    }
}
class MethodGeneric {
    // 泛型在访问修饰符后, 在返回值之前
    public <T> void getName(T name) {
        return;
    }
}
静态泛型方法

在静态方法中, 静态方法无法使用类中定义的泛型. 如果静态方法要使用泛型, 就必须在方法上定义.

public static <泛型表示符> void getName (泛型表示符 name) {
    
}
------------------------
public static <泛型表示符> 泛型表示符 getName (泛型表示符 name) {
    
}
------------------------
// 使用可变参数的泛型方法
public <泛型表示符> void method (泛型表示符... arr) {
    for (泛型表示符 each : arr) {
        System.out.println(each);    
    }
}

通配符和上下限定

无界通配符

“?” 表示类型通配符, 用于替代具体的类型. 它只能在"<>" 中使用, 可以解决具体类型不确定的问题.

这种表示任意类型的通配符为 “无界通配符”.

“?” 是类型实参, 而不是类型形参

类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。

可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

原文链接:https://blog.csdn.net/qq_40657585/article/details/99983426

————————————————

语法结构:

public void showFlag (Generic<?> generic) {
    }

代码实例:

public class Test04 {
    public static void main(String[] args) {
        ShowMsg showMsg = new ShowMsg();

        Generic<Integer> generic01 = new Generic<>();
        generic01.setFlag(10);
        showMsg.showFlag(generic01);

        Generic<Number> generic02 = new Generic<>();
        generic02.setFlag(20);
        showMsg.showFlag(generic02);

        Generic<String> generic03 = new Generic<>();
        generic03.setFlag("30");
        showMsg.showFlag(generic03);
    }
}

class ShowMsg {
    public void showFlag (Generic<?> generic) {
        System.out.println(generic.getFlag());
    }
}

class Generic <T> {
    private T flag;

    public void setFlag(T flag) {
        this.flag = flag;
    }

    public T getFlag() {
        return flag;
    }
}

问题: 20行, 除了使用 “?” 通配符之外, 还可以将代码写成

public void showFlag (Generic generic) {

即, 以使用泛型方法的形式, 解决类型不确定的问题,

但是不知道有什么区别

通配符的上限限定

上限限定是表示通配符的类型是T类, 以及T的子类或者T接口以及T接口的子接口.

该方法也适用于泛型的上限限定.

语法结构

// 通配符上限限定
public void showFlag (Generic<? extends Number> generic) {
}
// 泛型的上限限定
class Generic <T extends Number> {
    
}
通配符的下限限定

下限限定表示通配符的类型是T类以及T类的父类, 或者T接口以及T接口的父接口.

注意: 该方法不适用于泛型类

语法结构

public void showFlag (Generic<? super Integer> generic) {
}

泛型的总结

泛型主要用于编译阶段, 编译后生成的字节码class文件不包含泛型中的类型信息. 类型参数在编译后会被替换成Object类, 运行时虚拟机并不知道泛型, 因此, 使用泛型时, 有以下几种错误.

几种常见错误:

  1. 基本类型不能用于泛型

Test t; 这种写法是错误的, 我们可以使用对应的包装类; Test t;

  1. 不能通过类型参数创建对象.

T type = new T(); 运行时, 类型参数会被替换成Object, 无法创建T类型的对象, 容易引起误会.

容器(collection)

在这里插入图片描述

容器的结构

单例集合: 将数据一个一个的存储

List接口: 有序, 可重复

set接口: 无序, 不可重复

在这里插入图片描述

双例集合: 基于key与value的交够存储数据

在这里插入图片描述

Collection接口

Collection接口中的抽象方法

方法说明
add(Object element)增加元素到容器中
boolean remove(Object element)从容器中移除元素
boolean contains(Object element)容器中是否包含该元素
int size0容器中元素的数量
boolean isEmpty0容器是否为空
void clear()清空容器中所有元素
lterator iterator()获得迭代器,用于遍历所有元素
boolean containsAll(Collection c)本容器是否包含c容器中的所有元素
boolean addAll(Collection c)将容器c中所有元素增加到本容器
boolean removeAll(Collection c)移除本容器和容器c中都包含的元素
boolean retainAll(Collection c)取本容器和容器c中都包含的元素,移除非交集元素
Object[] toArray()转化成 Object 数组

List接口

除了Collection接口中的方法, List多了一些跟顺序(索引)有关的方法.

方法说明
void add (int index, Object element)在指定位置插入元素,以前元素全部后移一位
Object set (int index,Object element)修改指定位置的元素
Object get (int index)返回指定位置的元素
Object remove (int index)删除指定位置的元素,后面元素全部前移一位
int indexOf (Object o)返回第一个匹配元素的索引,如果没有该元素返回-1.
int lastindexOf (Object o)返回最后一个匹配元素的索引,如果没有该元素返回-1.
ArrayList类

将单例集合转换成数组

转换成 Object[] 数组和指定类型数组

public class TestList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("" + i);
        }
        System.out.println("原始数组: \n" + list);
        Object[] objs = list.toArray();
        System.out.println("Object类型数组: \n" + Arrays.toString(objs));
        String[] strs = list.toArray(new String[0]);
        System.out.println("指定类型数组: \n" + Arrays.toString(strs));
    }
}

在转换为指定类型数组时, 如果传入的数组长度小于集合中元素的个数, 那么方法内部会创建一个新的数组, 并返回

如果传入的数组长度大于集合的元素个数, 那么, 会执行arr[size] = null.arr是传入的数组, size是集合元素个数.

容器的并集操作

boolean addAll(Collection c)将原容器与指定容器进行并集操作, 默认是添加到原容器的末尾, 指定容器不得为空
boolean addAll(int index, Collection c)从index索引位置开始, 将指定容器中的元素添加到原容器

容器的交集操作

boolean retainAll(Collection c)只保留原容器中与指定容器的交集部分

容器的差集操作

boolean addAll(Collection c)保留在原容器, 但不在指定容器的元素

ArrayList类底层源码

底层是一个Object[] 数组, 创建时, 数组为空(延迟初始化), 添加元素后, 数组长度为10,

随后每次超过数组长度都会进行扩容

扩容操作的实现是创建一个更长的新数组, 长度为原来的1.5倍, 然后将原数组的元素拷贝到新数组.

Vector类

Vector底层使用数组实现的, 相关的方法斗加了同步检查(synchronize), 因此"线程安全, 效率低".

Stack容器

ect o) | 返回第一个匹配元素的索引,如果没有该元素返回-1. |
| int lastindexOf (Object o) | 返回最后一个匹配元素的索引,如果没有该元素返回-1. |

ArrayList类

将单例集合转换成数组

转换成 Object[] 数组和指定类型数组

public class TestList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("" + i);
        }
        System.out.println("原始数组: \n" + list);
        Object[] objs = list.toArray();
        System.out.println("Object类型数组: \n" + Arrays.toString(objs));
        String[] strs = list.toArray(new String[0]);
        System.out.println("指定类型数组: \n" + Arrays.toString(strs));
    }
}

在转换为指定类型数组时, 如果传入的数组长度小于集合中元素的个数, 那么方法内部会创建一个新的数组, 并返回

如果传入的数组长度大于集合的元素个数, 那么, 会执行arr[size] = null.arr是传入的数组, size是集合元素个数.

容器的并集操作

boolean addAll(Collection c)将原容器与指定容器进行并集操作, 默认是添加到原容器的末尾, 指定容器不得为空
boolean addAll(int index, Collection c)从index索引位置开始, 将指定容器中的元素添加到原容器

容器的交集操作

boolean retainAll(Collection c)只保留原容器中与指定容器的交集部分

容器的差集操作

boolean addAll(Collection c)保留在原容器, 但不在指定容器的元素

ArrayList类底层源码

底层是一个Object[] 数组, 创建时, 数组为空(延迟初始化), 添加元素后, 数组长度为10,

随后每次超过数组长度都会进行扩容

扩容操作的实现是创建一个更长的新数组, 长度为原来的1.5倍, 然后将原数组的元素拷贝到新数组.

Vector类

Vector底层使用数组实现的, 相关的方法斗加了同步检查(synchronize), 因此"线程安全, 效率低".

Stack容器

Stack栈容器, 是Vector的一个子类, 实现了一个标准的后进先出(LIFO : last in first out)的栈.

;