一、JDK 和 JRE

二.开发步骤

步骤:

1. 将 Java 代码编写到扩展名为 .java 的文件中。

2. 通过 javac 命令对该 java 文件进行编译。

3. 通过 java 命令对生成的 class 文件进行运行。

三、所在内存位置

  • 局部变量 -> 栈
  • 常量 -> 常量池
  • 类变量(静态变量) -> 静态存储区
  • new 出来的对象 -> 堆区

四、Java数据类型

上图说明:
1. java 数据类型分为两大类基本数据类型, 引用类型
2. 基本数据类型有8 中数值型[byte , short , int , long , float ,double] char , boolean
3. 引用类型[类,接口, 数组]

1、整型类型

2、浮点型

五、 基本数据类型转换

1、自动类型转化

//自动类型转换细节
public class AutoConvertDetail {
//编写一个main 方法
public static void main(String[] args) {
//细节1: 有多种类型的数据混合运算时,
//系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算
int n1 = 10; //ok
//float d1 = n1 + 1.1;//错误n1 + 1.1 => 结果类型是double
//double d1 = n1 + 1.1;//对n1 + 1.1 => 结果类型是double
float d1 = n1 + 1.1F;//对n1 + 1.1 => 结果类型是float
//细节2: 当我们把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时,
//就会报错,反之就会进行自动类型转换。
//
//int n2 = 1.1;//错误double -> int
//细节3: (byte, short) 和char 之间不会相互自动转换
//当把具体数赋给byte 时,(1)先判断该数是否在byte 范围内,如果是就可以
byte b1 = 10; //对, -128-127
// int n2 = 1; //n2 是int
// byte b2 = n2; //错误,原因: 如果是变量赋值,判断类型
// char c1 = b1; //错误, 原因byte 不能自动转成char
//细节4: byte,short,char 他们三者可以计算,在计算时首先转换为int 类型
byte b2 = 1;
byte b3 = 2;
short s1 = 1;
//short s2 = b2 + s1;//错, b2 + s1 => int
int s2 = b2 + s1;//对, b2 + s1 => int
//byte b4 = b2 + b3; //错误: b2 + b3 => int
//
//boolean 不参与转换
boolean pass = true;
//int num100 = pass;// boolean 不参与类型的自动转换
//自动提升原则: 表达式结果的类型自动提升为操作数中最大的类型
//看一道题
byte b4 = 1;
short s3 = 100;
int num200 = 1;
float num300 = 1.1F;
double num500 = b4 + s3 + num200 + num300; //float -> double
}
}

2、强制类型转化

介绍
自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符( ),但可能造成精度降低或溢出,格外要注意。

3、练习

六、运算符

% 取模,取余
 在% 的本质看一个公式!!!! a % b = a - a / b * b

/**
* 演示算术运算符的使用
*/
public class ArithmeticOperator {
//编写一个main 方法
public static void main(String[] args) {
// /使用
System.out.println(10 / 4); //从数学来看是2.5, java 中2
System.out.println(10.0 / 4); //java 是2.5
// 注释快捷键ctrl + /, 再次输入ctrl + / 取消注释
double d = 10 / 4;//java 中10 / 4 = 2, 2=>2.0
System.out.println(d);// 是2.0
// % 取模,取余
// 在% 的本质看一个公式!!!! a % b = a - a / b * b
// -10 % 3 => -10 - (-10) / 3 * 3 = -10 + 9 = -1
// 10 % -3 = 10 - 10 / (-3) * (-3) = 10 - 9 = 1
// -10 % -3 = (-10) - (-10) / (-3) * (-3) = -10 + 9 = -1
System.out.println(10 % 3); //1
System.out.println(-10 % 3); // -1
System.out.println(10 % -3); //1
System.out.println(-10 % -3);//-1
//++的使用
//
int i = 10;
i++;//自增等价于i = i + 1; => i = 11
++i;//自增等价于i = i + 1; => i = 12
System.out.println("i=" + i);//12
/*
作为表达式使用
前++:++i 先自增后赋值
后++:i++先赋值后自增
*/
int j = 8;
//int k = ++j; //等价j=j+1;k=j;
int k = j++; // 等价k =j;j=j+1;
System.out.println("k=" + k + "j=" + j);//8 9
}
}

练习

//练习
public class ArithmeticOperatorExercise01 {
//编写一个main 方法
public static void main(String[] args) {
// int i = 1;//i->1
// i = i++; //规则使用临时变量: (1) temp=i;(2) i=i+1;(3)i=temp;
// System.out.println(i); // 1
// int i=1;
// i=++i; //规则使用临时变量: (1) i=i+1;(2) temp=i;(3)i=temp;
// System.out.println(i); //2
//
// 测试输出
int i1 = 10;
int i2 = 20;
int i = i1++;
System.out.print("i="+i);//10
System.out.println("i2="+i2);//20
i = --i2;
System.out.print("i="+i);//19
System.out.println("i2="+i2);//19
}
}

七、逻辑运算符

1、&& 和& 使用区别

1) &&短路与:如果第一个条件为false,则第二个条件不会判断,最终结果为false,效率高
2) & 逻辑与:不管第一个条件是否为false,第二个条件都要判断,效率低

2、|| 和| 使用区别

1) ||短路或:如果第一个条件为true,则第二个条件不会判断,最终结果为true,效率高
2) | 逻辑或:不管第一个条件是否为true,第二个条件都要判断,效率低
3) 开发中,我们基本使用||

3、练习1:

4、练习2:

八、排序

(一)、内部排序:

指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择
式排序法和插入式排序法)。

(二)、外部排序:

数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。

(三)、冒泡排序:

冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

思路:

代码:

参考:Java十种排序实现

九、查找

1、顺序查找

2、二分法查找

十、方法的调用机制原理:(重要!-示意图!!!)

十一、成员方法传参机制(非常非常重要)

实列参考:Java的传参机制

1、基本数据类型的传参机制

内存图:

2、引用数据类型的传参机制

内存图:

3、练习

十二、构造器

 

十三、this关键字

1) this 关键字可以用来访问本类的属性、方法、构造器。
2) this 用于区分当前类的属性和局部变量。
3) 访问成员方法的语法:this.方法名(参数列表)。
4) 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)
5) this 不能在类定义的外部使用,只能在类定义的方法中使用。

十四、super关键字

十五、访问修饰符

修饰符 类内部 同一个包 不同包的子类 同一个工程
private Yes
default(缺省) Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes

十六、继承

继承的深入讨论/细节问题

1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2) 子类必须调用父类的构造器, 完成父类的初始化
3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解) 
4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7) java 所有类都是 Object 类的子类, Object 是所有类的基类
8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
9) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

十七、重写和重载

1、重写(Overriding)

重写发生在父类子类之间,比如所有类都是继承与Object类的,Object类中本身就有equals, hashcode,toString方法等.在任意子类中定义了重名和同样的参数列表就构成方法重写.

重写(override):一般都是表示子类和父类之间的关系,其主要的特征是:方法名相同,参数相同,但是具体的实现不同。

重写的特征:

(1):方法名必须相同,返回值类型必须相同

(2):参数列表必须相同

(3):访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。

(4):子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。

(5):构造方法不能被重写

2、重载(Overload)

重载发生在本类,方法名相同,参数列表不同,与返回值无关,只和方法名,参数列表,参数的类型有关.

重载(Overload):首先是位于一个类之中或者其子类中,具有相同的方法名,但是方法的参数不同,返回值类型可以相同也可以不同。

重载的特征:

(1):方法名必须相同

(2):方法的参数列表一定不一样。

(3):访问修饰符和返回值类型可以相同也可以不同。

3、区别

十八、多态

多态的前提是 :两个对象 ( 类 )存在继承关系

1、多态向上转型

1)本质:父类的引用指向了子类的对象

2)语法:父类类型     名称   =   new     子类类型();

3)特点:编译类型看左边,运行类型看右边。

可以调用父类中的所以成员(需要遵守访问权限)

不能调用子类中特有成员

最终运行看子类的具体实现

2、多态向下转型

1)语法:子类类型     名称   =   (子类类型) 父类引用;

2)只能强转父类的引用,不能强转父类的对象

3)要求父类的引用必须指向的是当前目标类型的对象

4)当向下转型后,可以调用子类类型中所有成员

十九、instanceOf

1、a instanceof A:判断对象a是否为A的实例,如果是返回true。

2、使用情景:避免在向下转型时出现异常,先进行判断。

二十、动态绑定与静态绑定机制

例题一

思考: 将子类方法sum()和sum1()注释后的结果是什么?

public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型System.out.println(a.sum());//40 -> 30
System.out.println(a.sum1());//?30-> 20
}
}class A {//父类public int i = 10;
//动态绑定机制:
public int sum() {//父类 sum()
return getI() + 10;//20 + 10
}
public int sum1() {//父类 sum1()
return i + 10;//10 + 10
}
public int getI() {//父类 getI
return i;
}
}class B extends A {//子类public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类 getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}

总结:1、动态绑定发生在多态里并且要进行重写,当调用方法后会动态绑定到子类的方法,当注释掉子类重写方法后会动态绑定找到父类的方法。2、属性没有动态绑定

例题二

class Son extends Person{//子类public void say(){System.out.println("这是子类的say()");}
}public class Person{//父类private void say(){System.out.println("这是父类的say()");}public static void main(String[] args){Person person = new Son();person.say();//这是父类的say()}
}

为什么是父类的say()?

原因:它与继承无关。当say()是private时,方法通过编译器直接静态绑定实现的,编译器不知道一个叫Son的子类以及其他子类,所以只能调用Person的方法。如果将父类private改为public,此时的结果为:这是子类的say().

总结:静态绑定:private、static、final修饰的方法都是静态绑定的。静态方法(因为静态方法与类本身相关而不是与对象相关)、final方法(final修饰符表示方法在子类中不能被覆盖)和private方法(private修饰符表示方法在子类中不可见)是不能实现动态绑定效果的。

二十一、==和 equals

1、==

1)==:既可以判断基本数据类型,有可以判断引用数据类型

2)==:如果判断基本数据类型,判断的是值是否相等

3)==:如果判断引用数据类型,判断的是地址是否相等,即判断是否为同一个对象

2、eqauls

1)equals:是Object类中的方法,只能判断引用类型

2)默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等

3、基本类型和包装类==和equals比较(重要)

实例参考:Java中基本类型和包装类的各类比较(==),以及包装类的对象缓存池

二十二、static、final、abstract、接口

1、static

总结:

1)static可以修饰属性和方法,不能修饰构造器

2)类方法和普通方法都随着类加载而加载:

类方法不能有this、super参数(this、super与对象有关)

普通方法隐含this、super参数

3)类方法可通过类名调用,也可通过对象名调用

4)类方法只能访问静态属性和静态方法(需遵守访问权限)

5)普通方法,既可以访问静态成员,也可以访问非静态成员

2、final

总结:

1)final可以修饰类、属性、方法,不能修饰构造器

2)final修饰的属性又叫常量,一般用大写XX_XX_XX来命名

3)final修饰的属性在定义时,必须赋初值,并且以后不能修改,赋值可以在以下位置:

①定义时:如public final double TAX_RATE=0.08;

②在构造器中

③在代码块中

4)如果final修饰的属性是静态的,则初始化位置只能是:

①定义时        ②在静态代码块  不能在构造器中赋值

5)final类不能继承,但可以实例化对象

6)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承

7)一般来说,如果一个类已经是final类,就没有必要将方法修饰成final方法

8)包装类(Integer、Double、Float、Boolean等都是final),String也是fianl类

3、abstract

总结:

1)abstract可以修饰类、方法,不能修饰构造器

2)抽象类不能被实例化

3)抽象类不一定要有abstract方法

4)一旦类包含了abstract方法,则这个类必须声明成abstract类

5)抽象类可以有任意成员(本质还是类),比如:非抽象方法、构造器、静态属性等

6)抽象方法不能有实体

7)如果一个类继承了抽象类,则它必须重写抽象类的所以方法,或者将这个类声明成abstract类

8)抽象方法不能使用private、final和static修饰,因为这些关键字都是和重写相违背

4、接口

总结:

1)接口不能被实例化

2)接口中所有方法是public,abstract方法,可以省略。例如:public abstract void get()可以定义为void get()

3)一个普通类实现接口,就必须实现该接口的所有方法

4)抽象类实现接口,可以不用实现接口方法

5)一个类可实现多个接口

6)接口中的属性是public static final的,可以省略,而且必须初始化。例如:int a = 1,实际上是public static final int a = 1

7)接口中属性的访问形式:接口名.属性名

8)接口不能继承其他类,但可以继承多个接口

9)接口的修饰符只能是public和默认的,这点和类的修饰符一样

二十三、 代码块

 

练习

public class CodeBlockDetail04 {
public static void main(String[] args) {//(1) 进行类的加载//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象//2.1 从子类的构造器开始//new B02();//对象new C02();
}
}
class A02 { //父类private static int n1 = getVal01();
static {
System.out.println("A02 的一个静态代码块..");//(2)
}
{
System.out.println("A02 的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器//隐藏//super()
//普通代码和普通属性的初始化...... System.out.println("A02 的构造器");//(7)
}
}
class C02 {
private int n1 = 100;
private static int n2 = 200;
private void m1() {
}
private static void m2() {
}static {
//静态代码块,只能调用静态成员//System.out.println(n1);错误System.out.println(n2);//ok
//m1();//错误m2();
}
{
//普通代码块,可以使用任意成员System.out.println(n1);
System.out.println(n2);//ok
m1();
m2();
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("B02 的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02 的第一个普通代码块..");//(9)}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
//一定要慢慢的去品.. public B02() {//构造器//隐藏了//super()
//普通代码块和普通属性的初始化... System.out.println("B02 的构造器");//(10)
// TODO Auto-generated constructor stub
}
}

二十四、内部类

如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2) 匿名内部类

定义在成员位置 (1) 成员内部类 (2) 静态内部类

1、局部内部类

/*** 演示局部内部类的使用*/
public class LocalInnerClass {public static void main(String[] args) {
//演示一遍Outer02 outer02 = new Outer02();outer02.m1();System.out.println("outer02 的 hashcode=" + outer02);}
}class Outer02 {//外部类private int n1 = 100;private void m2() {System.out.println("Outer02 m2()");}//私有方法public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用 final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的private int n1 = 800;public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);//100System.out.println("n1=" + n1 + " 内部类的 n1=" + Inner02.this.n1);//800System.out.println("Outer02.this hashcode=" + Outer02.this);//Outer02.this本质就是这个类m2();}}
//6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可Inner02 inner02 = new Inner02();inner02.f1();}
}

2、匿名内部类(重要)

public static void main(String[] args) {Interface01 interface01 = new Interface01() {//多态,底层就是创建一个类实现interfacepublic void show() {//重写show方法System.out.println("这里使用了匿名内部类");}};//调用接口方法interface01.show();
}//定义一个接口
public interface Interface01 {void show();
}

3、成员内部类

public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式// 第一种方式// outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员// 这就是一个语法,不要特别的纠结. Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { //外部类private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}//1.注意: 成员内部类,是定义在外部内的成员位置上//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员public class Inner08 {//成员内部类private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的//如果成员内部类的成员和外部类的成员重名,会遵守就近原则. //,可以通过 外部类名.this.属性 来访问外部类的成员System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1);
hi();
}
}
//方法,返回一个 Inner08 实例public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法public void t1() {
//使用成员内部类//创建成员内部类的对象,然后使用相关的方法Inner08 inner08 = new Inner08();
inner08.say();System.out.println(inner08.sal);
}
}

4、静态内部类

public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类//方式 1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式 2
//编写一个方法,可以返回静态内部类的对象实例. Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 { //外部类private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10 就是静态内部类//1. 放在外部类的成员位置//2. 使用 static 修饰//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员//5. 作用域 :同其他的成员,为整个类体static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)System.out.println(name + " 外部类 name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}

二十五、枚举类

1、自定义类实现枚举

public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
System.out.println(Season.SPRING);
}
}
//演示字定义枚举实现class Season {//类private String name;
private String desc;//描述//定义了四个对象, 固定. public static final Season SPRING = new Season("春天", "温暖");
public static final Season WINTER = new Season("冬天", "寒冷");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season SUMMER = new Season("夏天", "炎热");
//1. 将构造器私有化,目的防止 直接 new
//2. 去掉 setXxx 方法, 防止属性被修改//3. 在 Season 内部,直接创建固定的对象//4. 优化,可以加入 final 修饰符private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

2、使用 enum 关键字实现枚举

public class Enumeration03 {
public static void main(String[] args) {
System.out.println(Season2.AUTUMN);
System.out.println(Season2.SUMMER);
}
}
//演示使用 enum 关键字来实现枚举类enum Season2 {//类//定义了四个对象, 固定. // public static final Season SPRING = new Season("春天", "温暖");
// public static final Season WINTER = new Season("冬天", "寒冷");
// public static final Season AUTUMN = new Season("秋天", "凉爽");
// public static final Season SUMMER = new Season("夏天", "炎热");
//如果使用了 enum 来实现枚举类//1. 使用关键字 enum 替代 class
//2. public static final Season SPRING = new Season("春天", "温暖") 直接使用// SPRING("春天", "温暖") 解读 常量名(实参列表)
//3. 如果有多个常量(对象), 使用 ,号间隔即可//4. 如果使用 enum 来实现枚举,要求将定义常量对象,写在前面//5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/;
private String name;
private String desc;//描述private Season2() {//无参构造器}
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

二十六、包装类

1、包装类和基本数据的转换

基本类型 → 包装类:调用valueOf方法,比如Integer.valueOf()

包装类 → 基本类型:调用xxxValue方法,比如i.intValue()

public class Integer01 {
public static void main(String[] args) {
//演示 int <--> Integer 的装箱和拆箱//jdk5 前是手动装箱和拆箱//手动装箱 int->Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer1 = Integer.valueOf(n1);
//手动拆箱//Integer -> int
int i = integer.intValue();
//jdk5 后,就可以自动装箱和自动拆箱int n2 = 200;
//自动装箱 int->Integer
Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer->int
int n3 = integer2; //底层仍然使用的是 intValue()方法}
}

2、包装类型和 String 类型的相互转换

包装类 → String:1)调用toString方法。2)调用String.valueOf()方法

String → 包装类:调用parsexxx方法,比如Integer.parseInt(str)

public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer)->String
Integer i = 100;//自动装箱
//方式 1
String str1 = i + "";
//方式 2
String str2 = i.toString();
//方式 3
String str3 = String.valueOf(i);
//String -> 包装类(Integer)
String str4 = "12345";
Integer i2 = Integer.parseInt(str4);//使用到自动装箱Integer i3 = new Integer(str4);//构造器System.out.println("ok~~");
}
}

二十七、String、StringBuffer、StringBuilder

(一)、String

1、创建 String 对象的两种方式

内存分布图: 

练习一:

练习二

练习三

练习四

练习五

常用方法 

public class StringMethod02 {
public static void main(String[] args) {
// 1.toUpperCase 转换成大写String s = "heLLo";
System.out.println(s.toUpperCase());//HELLO
// 2.toLowerCase
System.out.println(s.toLowerCase());//hello
// 3.concat 拼接字符串String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
System.out.println(s1);//宝玉林黛玉薛宝钗 together
// 4.replace 替换字符串中的字符s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
//在 s1 中,将 所有的 林黛玉 替换成薛宝钗// s1.replace() 方法执行后,返回的结果才是替换过的. // 注意对 s1 没有任何影响String s11 = s1.replace("宝玉", "jack");
System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉// 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";// 1. 以 , 为标准对 poem 进行分割 , 返回一个数组// 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \
String[] split = poem.split(",");
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
System.out.println("==分割后内容===");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
// 6.toCharArray 转换成字符数组s = "happy";
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.println(chs[i]);
}
// 7.compareTo 比较两个字符串的大小,如果前者大,// 则返回正数,后者大,则返回负数,如果相等,返回 0// (1) 如果长度相同,并且每个字符也相同,就返回 0
// (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小// 就返回 if (c1 != c2) {
// return c1 - c2;
// }
// (3) 如果前面的部分都相同,就返回 str1.len - str2.len
String a = "jcck";// len = 3
String b = "jack";// len = 4
System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2 的值// 8.format 格式字符串/* 占位符有:
* %s 字符串 %c 字符 %d 整型 %.2f 浮点型*
*/
String name = "john";
int age = 10;
double score = 56.857;
char gender = '男';//将所有的信息都拼接在一个字符串. String info =
"我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我!";
System.out.println(info);//1. %s , %d , %.2f %c 称为占位符//2. 这些占位符由后面变量来替换//3. %s 表示后面由 字符串来替换//4. %d 是整数来替换//5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理//6. %c 使用 char 类型来替换String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
}
}

总结

1)String类是一个final类,不能被继承

2)String底层是一个final类型的char数组,例如:private final char[]

3)String实现了Serializable接口和Comparable接口

关于String底层不可变的理解

1)final char[]是指地址不可变,但是里面的值可以更改

2)当对字符串重新赋值时,需要重新指定内存区域。例如上图:0x1212不能修改为0x1213,只能在常量池中新建一个0x1213

(二)、StringBuffer、StringBuilder异同

方法

方法 说明
StringBuff append(String
str)
在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、
double、float、int、long、Object、String、StringBuff的变量
char charAt(int index) 获取index位置的字符
int length() 获取字符串的长度
int capacity() 获取底层保存字符串空间总的大小
void ensureCapacity(int
mininmumCapacity)
扩容
void setCharAt(int index,
char ch)
将index位置的字符设置为ch
int indexOf(String str) 返回str第一次出现的位置
int indexOf(String str, int
fromIndex)
从fromIndex位置开始查找str第一次出现的位置
int lastIndexOf(String str) 返回最后一次出现str的位置
int lastIndexOf(String str,
int fromIndex)
从fromIndex位置开始找str最后一次出现的位置
StringBuff insert(int
offset, String str)
在offset位置插入:八种基类类型 & String类型 & Object类型数据
StringBuffer
deleteCharAt(int index)
删除index位置字符
StringBuffer delete(int
start, int end)
删除[start, end)区间内的字符
StringBuffer replace(int
start, int end, String str)
将[start, end)位置的字符替换为str
String substring(int start) 从start开始一直到末尾的字符以String的方式返回
String substring(int
start,int end)
将[start, end)范围内的字符以String的方式返回
StringBuffer reverse() 反转字符串
String toString() 将所有字符按照String的方式返回

扩容机制

1)都继承AbstractStringBuffer类

2)底层创建了一个长度为16的字符数组

总结:StringBuffer sb1= new StringBuffer() 底层创建了一个长度为16的字符数组

3)创建指定长度的str(str<16和str>=16都行),容量为str.length + 16

总结:StringBuffer sb = new StringBuffer(“str”); 底层创建了一个 “str”.length()+16 的字节数组(前提是字符串里无中文字符,有中文字符就比较复杂,感兴趣的可以去看看源码,和coder这个编码有关)

4)当我们不断append添加字符,这个字符串长度超过这个数组长度保存不下了,这就需要给数组扩容,容量为:

  • 一次追加长度超过当前容量,则会按照 当前容量*2+2 扩容一次
  • 一次追加长度不仅超过初始容量,而且按照当前容量*2+2扩容一次也不够,其容量会直接扩容到与所添加的字符串长度相等的长度。之后再追加的话,还会按照当前容量*2+2进行扩容

StringBuffer和StringBuilder扩容机制详解

1、StringBuffer与StringBuilder中的方法和功能完全是等价的。

2、只是StringBuffer中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而StringBuilder没有这个修饰,可以被认为是线程不安全的。

3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

(三)、三者异同

String:不可变的字符序列:底层使用char[]存储
 StringBuffer:可变的字符序列:线程安全的,效率偏低:底层使用char[]存储
 StringBuilder:可变的字符序列:线程不安全的,效率高:底层使用char[]存储

二十八、日期类

1、第一代日期类

public class Date01 {
public static void main(String[] args) throws ParseException {//1. 获取当前系统时间//2. 这里的 Date 类是在 java.util 包//3. 默认输出的日期格式是国外的方式, 因此通常需要对格式进行转换Date d1 = new Date(); //获取当前系统时间System.out.println("当前日期=" + d1);
Date d2 = new Date(9234567); //通过指定毫秒数得到时间System.out.println("d2=" + d2); //获取某个时间对应的毫秒数//1. 创建 SimpleDateFormat 对象,可以指定相应的格式//2. 这里的格式使用的字母是规定好,不能乱写SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh:mm:ss ");
String format = sdf.format(d1); // format:将日期转换成指定格式的字符串System.out.println("当前日期=" + format);
//老韩解读//1. 可以把一个格式化的 String 转成对应的 Date
//2. 得到 Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换//3. 在把 String -> Date , 使用的 sdf 格式需要和你给的 String 的格式一样,否则会抛出转换异常String s = "1996 年 01 月 01 日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println("parse=" + sdf.format(parse));}
}

2、第三代日期类

因第二代日期类很少用到且问题很大,所有直接用第三代日期类

第二代日期类问题分析:

第三代日期类: 

public class LocalDate_ {
public static void main(String[] args) {
//第三代日期//1. 使用 now() 返回表示当前日期时间的 对象LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now()
System.out.println(ldt);
//2. 使用 DateTimeFormatter 对象来进行格式化// 创建 DateTimeFormatter 对象DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
System.out.println("格式化的日期=" + format);
System.out.println("年=" + ldt.getYear());
System.out.println("月=" + ldt.getMonth());
System.out.println("月=" + ldt.getMonthValue());
System.out.println("日=" + ldt.getDayOfMonth());
System.out.println("时=" + ldt.getHour());
System.out.println("分=" + ldt.getMinute());
System.out.println("秒=" + ldt.getSecond());
LocalDate now = LocalDate.now(); //可以获取年月日LocalTime now2 = LocalTime.now();//获取到时分秒//提供 plus 和 minus 方法可以对当前时间进行加或者减//看看 890 天后,是什么时候 把 年月日-时分秒LocalDateTime localDateTime = ldt.plusDays(890);
System.out.println("890 天后=" + dateTimeFormatter.format(localDateTime));
//看看在 3456 分钟前是什么时候,把 年月日-时分秒输出LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
System.out.println("3456 分钟前 日期=" + dateTimeFormatter.format(localDateTime2));
}

二十九、集合

1、集合的框架体系

2、集合常用方法

方法 说明

add(Object obj)

向集合中添加一个元素

addAll(Collection<? extends E>)

向集合中添加另一个集合

remove(Object obj)

移除一个数据

removeAll(Collection<? extends E>)

在一个集合中删除另一个集合。当然删除的是两个集合的交集

clear()

清除集合中全部的数据

size

获取集合中已经存储的元素

toArray()

将集合转换为数组

contains()

是否包含某一元素

containsAll()

是否包含某一种集合

isEmpty

是否为空

3、Collection 接口遍历元素方式

(1)迭代器

public class CollectionIterator {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("小李飞刀", "古龙", 5.1));
col.add(new Book("红楼梦", "曹雪芹", 34.6));
//System.out.println("col=" + col);//1. 先得到 col 对应的 迭代器Iterator iterator = col.iterator();
//2. 使用 while 循环遍历// while (iterator.hasNext()) {//判断是否还有数据// //返回下一个元素,类型是 Object
// Object obj = iterator.next();
// System.out.println("obj=" + obj);
// }
//老师教大家一个快捷键,快速生成 while => itit
//显示所有的快捷键的的快捷键 ctrl + j
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);}
//3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素// iterator.next();//NoSuchElementException
//4. 如果希望再次遍历,需要重置我们的迭代器iterator = col.iterator();
System.out.println("===第二次遍历===");
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
}
}
class Book {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}

(2)增强for

4、List接口基本介绍

List 的三种遍历方式 

(1)ArrayList 底层结构和源码分析

ArrayList 的注意事项

 ArrayList 的底层操作机制源码分析(重点,难点.)

//1.无参构造器
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;// 创建一个空的elementData数组}//2.list.add
public boolean add(E e) {ensureCapacityInternal(size + 1);  // 确定是否扩容elementData[size++] = e;    // 然后再执行赋值return true;}//3.确定minCapacity
private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {    // 如果elementData为空return Math.max(DEFAULT_CAPACITY, minCapacity);    // 返回10}return minCapacity;// 反之,返回minCapacity}//4.确定是否扩容
private void ensureExplicitCapacity(int minCapacity) {modCount++;    // 记录被修改次数if (minCapacity - elementData.length > 0)grow(minCapacity);}//5.用grow()扩容
private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);    if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);    // 使用Arrays.copyOf()方法}

(2)Vector 底层结构和源码剖析

//1. new Vector() 底层
public Vector() {this(10);
}补充:如果是 Vector vector = new Vector(8);走的方法:
public Vector(int initialCapacity) {this(initialCapacity, 0);
}2. vector.add(i)
2.1 //下面这个方法就添加数据到 vector 集合
public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;
}
2.2 //确定是否需要扩容 条件 : minCapacity - elementData.length>0
private void ensureCapacityHelper(int minCapacity) {if (minCapacity - elementData.length > 0)grow(minCapacity);
}
2.3 //如果 需要的数组大小 不够用,就扩容 , 扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ?
// capacityIncrement : oldCapacity);
//就是扩容两倍. private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement:oldCapacity);
if (newCapacity - minCapacity < 0)newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);
}

Vector 和 ArrayList 的比较

(3)LinkedList 底层结构

 模拟简单双向链表

public class LinkedList01 {
public static void main(String[] args) {
//模拟一个简单的双向链表Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老韩");
//连接三个结点,形成双向链表//jack -> tom -> hsp
jack.next = tom;
tom.next = hsp;
//hsp -> tom -> jack
hsp.pre = tom;
tom.pre = jack;
Node first = jack;//让 first 引用指向 jack,就是双向链表的头结点Node last = hsp; //让 last 引用指向 hsp,就是双向链表的尾结点//演示,从头到尾进行遍历System.out.println("===从头到尾进行遍历===");
while (true) {if(first == null) {break;
}
//输出 first 信息
System.out.println(first);
first = first.next;
}
//演示,从尾到头的遍历System.out.println("====从尾到头的遍历====");
while (true) {if(last == null) {break;
}
//输出 last 信息System.out.println(last);
last = last.pre;
}
//演示链表的添加对象/数据,是多么的方便//要求,是在 tom --------- 老韩直接,插入一个对象 smith
//1. 先创建一个 Node 结点,name 就是 smith
Node smith = new Node("smith");
//下面就把 smith 加入到双向链表了smith.next = hsp;
smith.pre = tom;
hsp.pre = smith;
tom.next = smith;
//让 first 再次指向 jack
first = jack;//让 first 引用指向 jack,就是双向链表的头结点System.out.println("===从头到尾进行遍历===");
while (true) {if(first == null) {break;
}
//输出 first 信息
System.out.println(first);
first = first.next;
}
last = hsp; //让 last 重新指向最后一个结点//演示,从尾到头的遍历System.out.println("====从尾到头的遍历====");
while (true) {if(last == null) {break;}
//输出 last 信息
System.out.println(last);
last = last.pre;}}
}
//定义一个 Node 类,Node 对象 表示双向链表的一个结点class Node {
public Object item; //真正存放数据public Node next; //指向后一个结点public Node pre; //指向前一个结点public Node(Object name) {this.item = name;
}
public String toString() {return "Node name=" + item;
}
}

ArrayList 和 LinkedList 比较

5、Set接口基本介绍

(1)HashSet的全面说明

(2)HashSet底层源码

@SuppressWarnings({"all"})
public class HashSetSource {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add("java");//到此位置,第 1 次 add 分析完毕. hashSet.add("php");//到此位置,第 2 次 add 分析完毕hashSet.add("java");
System.out.println("set=" + hashSet);1. 执行 HashSet()
public HashSet() {
map = new HashMap<>();
}
2. 执行 add()
public boolean add(E e) {//e = "java"
return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
}
3.执行 put() , 该方法会执行 hash(key) 得到 key 对应的 hash 值 算法 h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {//key = "java" value = PRESENT 共享return putVal(hash(key), key, value, false, true);
}4.执行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量//table 就是 HashMap 的一个数组,类型是 Node[]
//if 语句表示如果当前 table 是 null, 或者 大小=0
//就是第一次扩容,到 16 个空间. if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//(1)根据 key,得到 hash 去计算该 key 应该存放到 table 表的哪个索引位置//并把这个位置的对象,赋给 p
//(2)判断 p 是否为 null
//(2.1) 如果 p 为 null, 表示还没有存放元素, 就创建一个 Node (key="java",value=PRESENT)
//(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建Node<K,V> e; K k; //
//如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样//并且满足 下面两个条件之一:
//(1) 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象//(2) p 指向的 Node 结点的 key 的 equals() 和准备加入的 key 比较后相同//就不能加入if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//再判断 p 是不是一颗红黑树, //如果是一颗红黑树,就调用 putTreeVal , 来进行添加else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//如果 table 对应索引位置,已经是一个链表, 就使用 for 循环比较//(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后// 注意在把元素添加到链表后,立即判断 该链表是否已经达到 8 个结点// , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
// 注意,在转成红黑树时,要进行判断, 判断条件// if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
// resize();
// 如果上面条件成立,先 table 扩容. // 只有上面条件不成立时,才进行转成红黑树//(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接 break
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//size 就是我们每加入一个结点 Node(k,v,h,next), size++
if (++size > threshold)
resize();//扩容afterNodeInsertion(evict);
return null;
}
}
}

@SuppressWarnings({"all"})
public class HashSetIncrement {
public static void main(String[] args) {
/*
HashSet 底层是 HashMap, 第一次添加时,table 数组扩容到 16,
临界值(threshold)是 16*加载因子(loadFactor)是 0.75 = 12如果 table 数组使用到了临界值 12,就会扩容到 16 * 2 = 32,新的临界值就是 32*0.75 = 24, 依次类推*/
HashSet hashSet = new HashSet();
// for(int i = 1; i <= 100; i++) {
// hashSet.add(i);//1,2,3,4,5...100
// }
/*在 Java8 中, 如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是 8 ),
并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认 64),就会进行树化(红黑树),
否则仍然采用数组扩容机制*/
// for(int i = 1; i <= 12; i++) {
// hashSet.add(new A(i));//
// }
/*当我们向 hashset 增加一个元素,-> Node -> 加入 table , 就算是增加了一个 size++
*/
for(int i = 1; i <= 7; i++) {//在 table 的某一条链表上添加了 7 个 A 对象hashSet.add(new A(i));//}
for(int i = 1; i <= 7; i++) {//在 table 的另外一条链表上添加了 7 个 B 对象hashSet.add(new B(i));//
}
}
}
class B {
private int n;
public B(int n) {
this.n = n;
}
@Override
public int hashCode() {
return 200;
}
}
class A {
private int n;public A(int n) {
this.n = n;
}
@Override
public int hashCode() {
return 100;
}
}

(3)HashSet子类-----LinkedHashSet 

(4)TreeSet

public class TreeSet_ {
public static void main(String[] args) {//1. 当我们使用无参构造器,创建 TreeSet 时,仍然是无序的//2. 希望添加的元素,按照字符串大小来排序//3. 使用 TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
// 并指定排序规则//4. 简单看看源码/*
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到if (cpr != null) {//cpr 就是我们的匿名内部类(对象)do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入return t.setValue(value);
} while (t != null);
}
*/
// TreeSet treeSet = new TreeSet();
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//下面 调用 String 的 compareTo 方法进行字符串大小比较//如果要求加入的元素,按照长度大小排序//return ((String) o2).compareTo((String) o1);
return ((String) o1).length() - ((String) o2).length();
}
});
//添加数据. treeSet.add("jack");treeSet.add("tom");//3
treeSet.add("sp");
treeSet.add("a");
treeSet.add("abc");//3
System.out.println("treeSet=" + treeSet);
}
}
public class TreeMap_ {
public static void main(String[] args) {//使用默认的构造器,创建 TreeMap, 是无序的(也没有排序)
/*要求:按照传入的 k(String) 的大小进行排序*/
// TreeMap treeMap = new TreeMap();
TreeMap treeMap = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//按照传入的 k(String) 的大小进行排序//按照 K(String) 的长度大小排序//return ((String) o2).compareTo((String) o1);
return ((String) o2).length() - ((String) o1).length();
}
});
treeMap.put("jack", "杰克");
treeMap.put("tom", "汤姆");
treeMap.put("kristina", "克瑞斯提诺");
treeMap.put("smith", "斯密斯");
treeMap.put("hsp", "韩顺平");//加入不了System.out.println("treemap=" + treeMap);
/*1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 调用 put 方法2.1 第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
2.2 以后添加Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加return t.setValue(value);
} while (t != null);
}
*/
}
}

TreeSet底层原理

5、Map接口和常用方法基本介绍

6、Map 接口遍历方法

@SuppressWarnings({"all"})
public class MapFor {
public static void main(String[] args) {Map map = new HashMap();map.put("邓超", "孙俪");map.put("王宝强", "马蓉");map.put("宋喆", "马蓉");map.put("刘令博", null);map.put(null, "刘亦菲");map.put("鹿晗", "关晓彤");
//第一组: 先取出所有的Key , 通过Key 取出对应的ValueSet keyset = map.keySet();
//(1) 增强forSystem.out.println("-----第一种方式-------");for (Object key : keyset) {System.out.println(key + "-" + map.get(key));}
//(2) 迭代器System.out.println("----第二种方式--------");Iterator iterator = keyset.iterator();while (iterator.hasNext()) {Object key = iterator.next();System.out.println(key + "-" + map.get(key));}
//第二组: 把所有的values 取出Collection values = map.values();
//这里可以使用所有的Collections 使用的遍历方法
//(1) 增强forSystem.out.println("---取出所有的value 增强for----");for (Object value : values) {System.out.println(value);}
//(2) 迭代器System.out.println("---取出所有的value 迭代器----");Iterator iterator2 = values.iterator();while (iterator2.hasNext()) {Object value = iterator2.next();System.out.println(value);}
//第三组: 通过EntrySet 来获取k-vSet entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强forSystem.out.println("----使用EntrySet 的for 增强(第3 种)----");for (Object entry : entrySet) {
//将entry 转成Map.EntryMap.Entry m = (Map.Entry) entry;System.out.println(m.getKey() + "-" + m.getValue());}
//(2) 迭代器System.out.println("----使用EntrySet 的迭代器(第4 种)----");Iterator iterator3 = entrySet.iterator();while (iterator3.hasNext()) {Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型Map.EntryMap.Entry m = (Map.Entry) entry;System.out.println(m.getKey() + "-" + m.getValue());}
}

(1)Map 接口实现类-----HashMap

HashMap 小结

HashMap 底层机制及源码剖析 

HashMap 底层机制及源码剖析 

@SuppressWarnings({"all"})
public class HashMapSource1 {public static void main(String[] args) {HashMap map = new HashMap();map.put("java", 10);//okmap.put("php", 10);//okmap.put("java", 20);//替换valueSystem.out.println("map=" + map);//
1. 执行构造器new HashMap()
初始化加载因子loadfactor = 0.75HashMap$Node[] table = null
2. 执行put 调用hash 方法,计算key 的hash 值(h = key.hashCode()) ^ (h >>> 16)public V put(K key, V value) {//K = "java" value = 10return putVal(hash(key), key, value, false, true);}
3. 执行putValfinal V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
//如果底层的table 数组为null, 或者length =0 , 就扩容到16if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;
//取出hash 值对应的table 的索引位置的Node, 如果为null, 就直接把加入的k-v
//, 创建成一个Node ,加入该位置即可if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;//辅助变量
// 如果table 的索引位置的key 的hash 相同和新的key 的hash 值相同,
// 并满足(table 现有的结点的key 和准备添加的key 是同一个对象|| equals 返回真)
// 就认为不能加入新的k-vif (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)//如果当前的table 的已有的Node 是红黑树,就按照红        黑树的方式处理e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {
//如果找到的结点,后面是链表,就循环比较for (int binCount = 0; ; ++binCount) {//死循环if ((e = p.next) == null) {//如果整个链表,没有和他相同,就加到该链表的最后p.next = newNode(hash, key, value, null);
//加入后,判断当前链表的个数,是否已经到8 个,到8 个,后
//就调用treeifyBin 方法进行红黑树的转换if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash && //如果在循环比较过程中,发现有相同,就break,就只是替换value((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value; //替换,key 对应valueafterNodeAccess(e);return oldValue;}}++modCount;//每增加一个Node ,就size++if (++size > threshold[12-24-48])//如size > 临界值,就扩容resize();afterNodeInsertion(evict);return null;}
5. 关于树化(转成红黑树)
//如果table 为null ,或者大小还没有到64,暂时不树化,而是进行扩容.
//否则才会真正的树化-> 剪枝final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();}}
}

模拟HashMap触发内容、树化情况,Debug验证

@SuppressWarnings({"all"})
public class HashMapSource2 {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
for(int i = 1; i <= 12; i++) {
hashMap.put(i, "hello");
}
hashMap.put("aaa", "bbb");
System.out.println("hashMap=" + hashMap);//12 个k-v
//布置一个任务,自己设计代码去验证,table 的扩容
//0 -> 16(12) -> 32(24) -> 64(64*0.75=48)-> 128 (96) ->
//自己设计程序,验证-》增强自己阅读源码能力. 看别人代码.
}
}
class A {
private int num;
public A(int num) {
this.num = num;
}
//所有的A 对象的hashCode 都是100
// @Override
// public int hashCode() {
// return 100;
// }
@Override
public String toString() {
return "\nA{" +
"num=" + num +
'}';
}
}

(2)Map 接口实现类-----Hashtable

HashTable 的基本介绍

HashTable底层实现

Hashtable 和 HashMap 对比 

(3)Map 接口实现类-Properties

基本使用 

public class Properties_ {
public static void main(String[] args) {//1. Properties 继承 Hashtable
//2. 可以通过 k-v 存放数据,当然 key 和 value 不能为 null
//增加Properties properties = new Properties();
//properties.put(null, "abc");//抛出 空指针异常//properties.put("abc", null); //抛出 空指针异常properties.put("john", 100);//k-v
properties.put("lucy", 100);
properties.put("lic", 100);
properties.put("lic", 88);//如果有相同的 key , value 被替换System.out.println("properties=" + properties);//通过 k 获取对应值System.out.println(properties.get("lic"));//88
//删除properties.remove("lic");
System.out.println("properties=" + properties);
//修改properties.put("john", "约翰");
System.out.println("properties=" + properties);}
}

三十、泛型

Java基础细节(持续更新中)相关推荐

  1. JAVA基础(持续更新中)

    JAVA基础 2020年11月27日 21:01 1 预科 a. 什么是计算机 能够按照程序运行,自动.高速处理海量数据的现代化智能电子设备. 由硬件常见的形式有台式计算机.笔记本计算机.大型计算机等 ...

  2. JAVA面试大全(持续更新中...)

    本文旨在收集Java面试过程中出现的问题,力求全面,仅作学习交流,欢迎补充,持续更新中-,部分段落选取自网上,部分引用文章已标注,部分已记不清了,如侵权,联系本人 Java基础 1.面向对象的概述 面 ...

  3. 幻想-FLEX 3基础视频教程 持续更新中

    欢迎点击此处订阅本Blog title="RSS 2.0" type="application/rss+xml" href="http://feed. ...

  4. Java基础入门(持续更新)

    目录 Java基础入门1 1.Hello Java 1.1 Java简介 1.2 Java体系与特点 java的特性 1.3 Java 跨平台原理 Java 技术两种核心机制 Java 虚拟机(JVM ...

  5. Java知识点汇总 持续更新中~~~

    一.什么是面向对象? 是基于面向过程而言,面向对象是将功能通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节. 二.标识符的格式? 1.可以使用字母.数字._.$来组成,不能使用特殊符号. ...

  6. Node.js零基础自学(持续更新中)

    1. Node.js时基于Chrome V8 引擎的JavaScript运行环境.官网:Node.jsNode.js® is a JavaScript runtime built on Chrome' ...

  7. java基础必备持续更新优化

    java的程序分有2类:     1.嵌入在网页中,通过浏览器运行的程序,被称为Applet,译为小应用程序. 2.除1之外Java程序,被称为Application,译为应用程序. 第一个java ...

  8. Java学习笔记(持续更新中)

    文章目录 项目实战 mall项目(SpringBoot项目) 1. 添加Swagger-UI配置,修改MyBatis Generator注释的生成规则 2. redis基础配置 3. SpringSe ...

  9. 前端使用Canvas绘图(基础知识)--持续更新中

    文章目录 前言 canvas文档 一.canvas代码提示(插件和注释) 1.1.使用插件方式(推荐这种方式) =>canvas-snippets 1.2.使用注释方式 二.初始canvas 2 ...

  10. 微信支付先享后付java实现(持续更新中)

    由于网上资料少,而且微信的文档我知道的就有4-5个版本,各个不一样,所以做这个的时候还是挺坑的,还好已经实现了,特此记录一下,让后来人,有个参考 一.先注册微信商户平台,那一堆乱七八糟的,就不说了,自 ...

最新文章

  1. linux 检查权限,检查目录下 文件的权限-linux shell脚本,
  2. Beep()之我迷糊了……
  3. Silverlight游戏设计(Game Design):目录
  4. C++ 中的 new/delete 和 new[]/delete[]
  5. 如何在typescript中使用axios来封装一个HttpClient类
  6. linux htb 源代码,LINUX TC:HTB相关源码
  7. 路由器端口映射实现远程桌面
  8. matplotlib+numpy绘制二维条形直方图
  9. java中after什么意思_Java中的即时isAfter()方法
  10. 评价目标检测区域的准确性——IoU
  11. java 状态机_Java 数据持久化系列之池化技术
  12. 腾讯自动驾驶新动作!与现代合作开发无人车系统
  13. Java基础之如何修改字符串?
  14. Requirements Analysis with 'pseud-Formal' Method
  15. matlab 异常,Matlab 2017b 异常信息。程序奔溃。
  16. ES6中的模块化编程
  17. 做课题与科研项目常用的研究方法
  18. Python OOP 项目实践:烤地瓜,搬家具
  19. 2.2.7Python-异常处理
  20. safair浏览器 在回调中跳转 window.open 打不开页面 但是有判断,跳转不了

热门文章

  1. Linux远程访问Windows
  2. 2023年2月21日
  3. Cocoapods: 打造本地 pod 库
  4. leo-水电收费管理系统
  5. 如何让一个vue项目支持多语言(vue-i18n) 打包后依然可以修改语言包 并且修改后不需要重新打包
  6. es7新特性和es8新特性
  7. win10 PaddleOCR c++ cpu部署
  8. 常用域名后缀所代表的含义
  9. OKHttp3 Cookie 持久化
  10. Excel学习笔记:P37-这是什么巫术?我弄半天的表格,结果旁边的同事弹指一挥间就全部做好了!