历史

Java语言开发者 \ Java之父: 詹姆士.高士林(Gosling)
Sun Microsystem(太阳微系统)
被收购,Oracle公司(甲骨文): Oracle

  • Java版本:

    • JavaSE: Java Standard Edition 标准版
    • JavaEE: Java Enterprise Editition 企业版 并发技术
    • JavaME: Java Mobile Edition 移动版
  • JDK,JRE,JVM

    • JRE:Java Runtime Environment (Java运行时) :就是java运行的环境
    • JDK: Java Development Kit (Java开发工具包):
      里面有java开发所需的编译器,运行器,函数库等……
      JDK中包含JRE。
    • JVM : Java Virtual Machine (Java虚拟机)
      是一个软件程序,运行字节码文件的,并且不能单独
      好处,可以实现跨平台运行

Java语言特性

  • 简单性:Java舍弃了C++中难以掌握并不安全的功能,如:指针、多继承等,Java语言底层是C++实现的
  • 面向对象:Java和C++一样,是一种面向对象编程语言
  • 安全性:如:运行时堆栈溢出,强制类型检查
  • 健壮性:Java语言在运行过程中产生的垃圾会自动回收,简称GC机制
  • 可移植性:Java程序编译一次,不做任何修改时到处运行,也就是跨平台

术语

OP: 面向过程
OOA: 面向对象分析
OOD: 面向对象设计
OOP: 面向对象编程
team: 团队
leader: 领导
PM:
1.项目经理: 有全盘项目管理,开发能力,沟通能力强
2.产品经理: 产品经理对软件产品负责,一般是在开发前,他负责与客户沟通。了解和收集客户需求,就开始画图(设计产品原型) UI

面向对象 与 面向过程的区别

  • 什么是面向过程
    概念: 做事情有一定的顺序或因果关系,那么这种程序设计为面向过程的程序设计。
    优点:简单,开发时间短,无需团队直接就可以开发。无需任何软件开发模型。
    缺点:耦合度高。

  • 什么是面向对象
    概念:把生活中每个实物看做一个对象,然后把各个对象联系起来,经过一系列动作,来整体做一件事。
    优点: 把生活中的实物理解对对象,对象之间有联系,开发就去找对象,耦合度低。
    适合大型项目,彼此有联系但互不影响。
    缺点: 开发周期长,文档多。

    看代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Test1 {
    public static void main(String[] args){
    int n=10;
    print(n);
    System.out.println(n);
    }
    public static void print(int n) {
    n++;
    }
    }
    //最后输出10

  • 概念:类似于windows中的文件夹
  • 作用是: 避免同名文件的冲突
  • 关键字:package
  • 语法: package 包名;
  • 取名的规范: package a;或者 按照域名的反写com.bjpowernode.it.oa;(域名.部门名.项目名;)

变量

变量的内存分析图

  • 概念: 变量是一个盒子,盒子的大小由数据类型来决定,盒子放入的是数据,数据的内容是可以变化的。
  • 分类:
    • 局部变量:在函数中定义的变量,有效范围在整个函数中有效。
    • 全局变量:在类中定义的变量,有效范围在整个类中有效。
  • 有作用域(使用范围)
    1
    2
    3
    4
    { //这一对大括号就是一个作用域,变量a在此大括号中有效.
    int a=1;
    } //这一对大括号是叫代码块.
    System.out.println(a);//此处不能输出

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test1 {
public static void main(String[] args) {
int a;
System.out.println(a); //编译出错
}
}
//局部变量必须初始化(给变量赋值),全局变量可以不用赋值,因为它有默认值。




public class Test2 {
public static void main(String[] args) {
int a=1;
{
int a=3;
System.out.println(a);
}

{
int a=2;
System.out.println(a);
}
}
}
//编译出错,int a 只能定义一遍。

标识符

  • 概念:是程序员取的合法的一个名字,这个名字可以用在类,函数,变量处等等。
  • 命名规则:
    • 标识符必须以字母开头
    • 标识符组成:字母,数字,下划线,$
    • 标识符不能以数字开头,可以组合4大部分。
    • 标识符可以取中文吗? 可以的,中文也是字符,和字母类似,直接使用。但是项目中不用
    • 标识符用在类处,要求类名首字母大写。
    • 不能用关键字
  • 标识符规范
    • 驼峰、下划线命名
    • 望文生义

Java代码执行机制

Java加载与执行
分为两个阶段:

  • 编译阶段
    • 编写Java源代码,保存为.java的文件
    • 打开命令提示符—>写入一个javac 源文件名
    • 编译完成后,会形成一个.class文件,为字节码文件。
  • 运行阶段
    • 通过写入一条命令:java 类名
    • 这个.class文件被类加载器(classloader)进行加载,交给了虚拟机(JVM),是一个程序,软件,它开始读取.class文件中的类信息,把.class文件—>翻译成了0101数据(二进制)
    • 二进制数据被操作系统识别(windows),交给相应的硬件进行执行,最终输出结果。

dos下

  • javadoc:生成文档注释命令
    例子:
    把Helloworld的注释文档放进aaa文件夹下
    C:> javadoc -d aaa HelloWorld.java
  • jar:打压缩包(.jar)
    例子:
    把Helloworld.java和Helloworld2.java打包压缩民命为hello.jar
    C:> jar cvf hello.jar Helloworld.java Helloworld2.java
  • 导入包
    HelloWorld.java导入hello文件夹下的另一个HelloWorld.java
    C:> javac -d . HelloWorld.java
    C:> java hello.HelloWorld

计算机编码

  • ASCII码: 美国标准信息交换码:只支持英文 ,不支持中文
    比如: a: 97 A: 65
  • ISO-8859-1: 支持拉丁文,英文,不支持中文
  • GBK : 所有中文编码统称: 支持简体(GB2312)和繁体,固定长度中文
  • UTF-8:支持中文,英文 :可变长度中文
  • Unicode:支持任何一种语言,包括中文,让中文在任何一台电脑上都能正确显示。
    语法:\u十六进制数字
    比如:\u6702;

进制表示

二进制,八进制,十六进制在计算机编程时,如何表示
二进制: 0b
八进制: 0
十六进制: 0x

例子:
二进制以零b开头如:int a = 0b0101010;
八进制以零开头如:int a = 012345;
十六进制以零x开头如:int a = 0x12345;

数据类型之间转换

  • 自动类型转换(隐式类型转换):
    要求:要转换的两种类型必须兼容
    低优先级类型–>高优先级类型(byte–>int)
  • 强制类型转换:
    要求:要转换的两种类型必须兼容
    高优先级类型–>低优先级类型(int–>byte)
  • 当多种数据类型混合运算时,有限取范围大的数据类型

语法:(要强制转换的类型)数据
例子:(int)3.14

顺序与转换

  1. 基本数据类型中除了布尔数据类型以外,剩余7种数据类型之间是可以相互转换的
  2. 取值范围小的数据类型是可以直接给取值范围大的数据类型赋值,构成自动类型转换,也叫做隐式类型转换
  3. 取值范围大的数据类型不能直接给取值范围小的类型赋值,则必须进行强制类型转换,也叫做显式类型转换,可能会存在精度有丢失
  4. 当对byte、short、char类型赋值时,如果没有超出当前数据类型取值范围,则是可以直接赋整数类型的值
  5. 当对byte、short、char类型进行混合运算时,则先转为int类型,然后再进行计算
  6. 当多种数据类型进行混合运算时,结果为当前运算中取值范围大的数据类型

字面量

  • 概念:就是数据。
  • 数据分类:
    • 基本数据类型:
      并且有优先级
      数据类型 所占的字节大小 默认值
      byte:字节 1 0
      short:短整形 2 0
      int:整形 4 0
      long:长整形 8 0L
      float:单精度 4 0.0f
      double:双精度 8 0.0
      boolean:布尔 1 false
      char:字符 2 \u0000
    • 引用数据类型:
      String:字符串
      Array:数组
      class:类类型
      interface:接口类型
      enum:枚举类型
      。。。。

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a=1;
int b=2;

System.out.println("a和b和是"+a+b);
//输出:a和b和是12(拼接优先级比运算优先级大)



byte a=1;
short b=10;
char c='a';

System.out.println(a+b+c); //108
// 多种类型同时放在一起时,它们会先转成整形int类型,然后进行操作


int a=020_28283223; //整数中可以带下划线,新版本jdk特性决定的。

运算符

  • 概念: 一些符号,这些可以参与运算,逻辑比较等操作。

  • 算术运算符:
    执行基本数学运算的符号: +,-,*,/(除法),%(取余数)

  • 逻辑运算符:
    执行逻辑判断:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    &(按位与,逻辑与) 
    10 & 6 :按位与 (换成二进制)
    条件1 & 条件2

    |(按位或,逻辑或)
    10 | 6 :按位或(换成二进制)

    条件1|条件2: 逻辑或
    !(非)
    &&(短路与)
    ||(短路或)

    ^(异或)
    10 ^ 6: 换成二进制,不同的为1,相同的为0
    逻辑异或^
    当两边的值不同时,则结果为true,否则为false
    true ^ true 结果为false
    true ^ false 结果为true
    false ^ false 结果为false
    false ^ true 接为true
  • 比较运算符: >,>=,<,<=,!=

  • 移位运算符 :
    转换成二进制后位移

    • 往右移位: >>, >>>
    • 往左移位: <<
      1
      2
      3
      如:10>>1 //输出:5      解释:10转换成二进制后向右移动一位
      //相当于除以2
      10>>2 //相当于除以4

以后项目中要做除法运算,使用移位了。

  • 三目运算符
    语法格式:条件 ? 代码1 : 代码2
    首先判断条件,如果条件为true,则执行代码1,否则执行代码2

  • 赋值运算符 =,+=,-=,*=,/=,%=

  • +=和-=都有强转的概念

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //例子1:
    int a=1;
    int b=2;

    int c=a++; //1 a=2
    int d=++b; //d=3 b=3

    //最后输出2 3 1 3
    System.out.println(a);
    System.out.println(b);
    System.out.println(c);
    System.out.println(d);

    //例子2:
    int a=1;
    int b=2;

    a++; //2
    ++b; //3

    //最后输出2 4
    System.out.println(a++);
    System.out.println(++b);

判断语句 与 循环语句

If(布尔表达式)
单if
If…else
If…else if …else(多重if选择结构)
If…if(if嵌套)

1
2
3
4
5
6
7
8
9
// 当做等值操作时,则就可以使用switch case完成
Swith(变量/表达式){
case 常量:
满足case需要做的事情;
break//退出
default:(比较灵活不一定写在最后)
以上都不满足最后才执行
break//退出
}
  • 循环概念:
    有规律地重复做同样的事情
    外层循环做一次,内层循环做一遍

  • 步骤:
    书写循环变量,赋予初始值
    书写循环条件
    书写循环体:循环做的事情
    设计循环退出的条件 ,防止循环死掉 死循环

  • while和do…while循环区别?
    while和do..while语法不同。
    while先判断条件是否满足,再去执行,do…while先去执行,再判断条件是否满足。不管怎样,do…while一定会执行一次

  • 循环关键字:
    break: 退出当前循环结构
    continue: 结束本轮(次)循环,进入下一轮循环

  • 循环标签(尽量不用)
    使用意义:在二重循环结构中,可以直接退出最外层的循环

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//此处的a,b为循环标签(指针的方式),实际项目建议不要使用循环标签. 

a:for(int i=0;i<3;i++) {
b:for(int j=0;j<2;j++) {
if(j==1) {
break a; //一次直接退出a的外层for循环,
}
System.out.println("i="+i+",j="+j);
}
}


//判断使出了什么?
int i=0,j=0;
for(;i<7;i++) {
if(j>4) {
System.out.println("**");
continue;
}
System.out.println("*");
j++;
}
//分析最后输出
//i j
// 0 1 *
// 1 2 *
// 2 3 *
// 3 4 *
// 4 5 *
// 5 5 **
// 6 5 **




int a=1;
switch(a) {

case 1:
System.out.println("a");
case 2:
System.out.println("b");
break;

default:
System.out.println("c");
break;
//输出ab,构成穿透




int i=1;
for(;i<10;i++) {
System.out.println(i);
}
//循环输出1~9



for(int i=1;;i++) {
System.out.println(i);
}
//死循环,i不停地加,条件默认为true

for(int i=1;i<10;) {
System.out.println(i);
}
//死循环,一直输出1,没有退出条件

for(;;) {
System.out.println("hello");
}
//死循环,一直输出hello



//if可以没有大括号,但要紧接着语句

int a = 1;
if (a > 0)
System.out.println("输出1");
System.out.println("输出2");
else
System.out.println("输出3");

方法(函数)

概念: 方法也叫函数,是一段用来完成特定功能的代码。
作用: 代码可以重复利用 ,提高复用性
语法: [访问修饰符] static 方法返回类型 方法名(参数列表) { }

  • 方法是可以相互调用的
  • 方法定义不能嵌套
  • java中方法返回值只能返回单个值
  • 逻辑代码不能写在类体中,必须写在方法内部

当方法带有返回值类型时,如果在case或者default中编写return则就不需要再编写break,否则出现编译错误;当方法带有返回值类型必须编写返回严谨,否则就出现编译错误。

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void p(){
public static void p2(){ //方法定义不能嵌套
System.out.println("我是p2");
}
}
p();



// 逻辑代码`不能`写在类体中,必须写在方法内部
public class Test1 {
int x=2;
if(x>0) {
System.out.println(x);
}
public static void p(){
System.out.println(x);
}
}




// java中方法返回值只能返回`单个值`。
public static int p(){

int width=1;
int height=2;

return width,height;
}

p();

递归

方法自己调用自己

递归效率低,容易栈溢出,不建议使用

解决栈溢出的方案:

  1. 减少输出的次数
    设置相应的条件,规定输出的次数,防止溢出
  2. 栈的空间大小调整
    不适合于初级工程师

递归调用实际中还是有应用场合:
一些经典的数学运算
做目录的列出,可以递归,比如当前盘下的所有内容

练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
//使用递归完成,计算n!
public static int mul(int n){
//判断形参n的值,如果n是0 或者n是1,则直接返回1
if(n == 0 || n == 1){
return 1;
}
return n * mul(n - 1);
}
public static void main(String[] args) {
System.out.println(Test.mul(3)); //例如3!
}
}

关键字:return

带返回类型的方法中: return 要返回的值;
无返回类型的方法中: 直接写return即可; 代表是终止方法执行。

重载 与 重写

方法重载(overload)

  • 意义:解决方法命名上的繁琐的问题,用同一个名字,就可以直接操作。

满足三条件:
同一个类中
方法名相同
参数列表不同(个数、顺序、类型)

方法重写\覆盖(override)

概念: 发生在继承结构中,把父类中继承过来的同名方法改变里面的内容。如果子类中的方法与父类中的方法签名相同,只需要改变内容即可。
即外壳不变,核心重写!

签名相同:方法名相同,方法的参数列表相同,方法的返回类型相同,方法的访问修饰符相同。

如果父类中书写了静态方法,就不讨论方法重写

区别

重写是发生在继承中,重载是发生在本类中

重写必须一切都要相同,从方法名,参数类型,顺序,返回类型,访问修饰符
重载是方法名相同,方法的参数个数不同,类型不同,顺序不同。与返回类型,访问修饰符无关

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
public static void main(String[] args) {
B b=new B();
b.m1();
}
}

class A {
public static void m1() {
System.out.println("m1方法");
}
}

class B extends A {
public static void m1(){
System.out.println("m1方法");
}
}
// 构成重载了吗?没有。重写针对的是实例方法。静态方法不讨论方法重写。


// 题二
class Mouse {}
class Jerry extends Mouse { }

class A {
public Mouse show() {
return new Mouse();
}
}

class B extends A {
public Jerry show(){
return new Jerry();
}
}
// 构成了重载。子类中方法的返回类型如果是父类同名方法的返回类型的子类,则子类中的这个方法为方法重写。

关键字:static 与 代码块

静态代码块、属性、方法在类加载时就开始加载,所以是类级别

可以修饰属性、方法、代码块、内部类以及实现静态导入

  • 修饰属性
    public static int a=1; //静态变量,也叫静态属性
  • 修饰方法
    public static void show(){} ////静态方法
  • 修饰代码块
    static{ } //当类加载到内存时执行静态代码块,并且只执行一次
  • 修饰内部类(一般不用)
    1
    2
    3
    4
    5
    public static void show(){
    Inner2 i = new Inner2();
    }
    static class Inner2{ //称为静态内部类,一般在外部类的静态方法中使用
    }
  • static关键字可以实现静态导入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyClass {
    static int y = 100;
    }


    import static MyClass.y;
    public class Test {
    public static void main(String[] args) {
    //注意:当在一个类中,如果想直接使用其他类中的静态变量,则必须要实现静态导入
    System.out.println(y);
    }
    }
  • 与final配合使用
    1
    2
    3
    4
    5
    6
    public class MyClass {
    static final int A = 100; //表示常量,也就是值不能更改,因此建议:final关键字与static关键字配合使用,原因:当编写static关键字在内存中只存放一次,并且该类所有对象都可以共享,也就是节省空间

    static final double PI = 3.14;
    final static double MY_PI = 3.14159;
    }

静态方法中不能访问非静态成员方法和变量

多个代码块时,自上而下,按照顺序依次加载。
静态变量初始化可以在构造方法中进行初始化,但不建议

关键字:final

概念:最终的
可以修饰三个部分:

  • 属性:
    属性为最终属性: 代表此属性内容不可更改
    语法: public final String name=”1”;
  • 方法:
    方法为最终方法:代表此方法不能被重写
  • 类:
    此类为最终类:代表此类不能被继承

加入static ,做成静态常量,常量名全大写
例子:public final static String NAME=”abc”;

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class Animal {
public void show(){
System.out.println("父类输出");
}
}

class Dog extends Animal {
int age;
public void show(){
System.out.println("子类输出");
}

}

public class Test01 {
public static void main(String[] args) {
final Animal a=new Dog();
a.age=18;//报错,因为Animal没定义age
a.show();
}
}

访问权限

公共类和非公共类
当一个类前面编写public修饰符,称为公共类,则该类可以在任意包中使用
当一个类前面没有编写public修饰符,称为非公共类,则该类只能在当前包中使用

成员目前所学知识点中包含:成员变量和成员方法

访问权限 在本类中 在当前包中 不同包的子类 任意包任意类
private私有的 可以使用 不可以使用 不可以使用 不可以使用
默认的 可以使用 可以使用 不可以使用 不可以使用
protected受保护的 可以使用 可以使用 可以使用 不可以使用
public公共的 可以使用 可以使用 可以使用 可以使用

例子:

1
2
3
4
private int ages; // 私有的,如封装便需要用到
String name;

// 大多数情况下,方法的访问权限使用public

面向对象

  • Java中有3种变量:
    实例变量:存在于堆内存中
    静态变量:存在于本地内存区域(方法区)中
    局部变量:存在与栈内存中
  • 面向对象
    对象:是实体,是生活中真实存在的事物。
    类:是模板,是一个抽象的概念。把对象共同的特征和行为抽取出来。

    实例:对象另一种说法
    实例化:类产生对象的过程 类—>对象
    抽象:对象的共同特征和行为抽取出来,形成模板的过程。 对象—>类

  • 属性
    概念:也叫特征;是全局变量也叫实例变量。
    特点:属性以数据的方式呈现由对象所有,且每个对象都有一份实例变量有默认值,引用数据类型去做一个类的属性

  • 行为
    行为也叫方法,代表此类的一个动作。
    这种方法也叫实例方法。
    这种方法由对象来调用,由对象所有。
    方法取名一般使用动词。

  • 构造方法
    概念: 是与类同名的方法,也叫构造函数,也叫构造器
    特点:方法名与类同名
    比如: Student();

    作用:用来给属性赋值(初始化属性) 给其他对象初始化.
    语法: [访问修饰符] 方法名(){ 方法体;}

  • 缺省构造器: 无参构造方法
    如果类中没有声明任何构造方法,则在实例化时,会默认加载无参构造方法。
    支持方法重载
    在new对象时,进行调用

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 题一
public class Test {
String name;
}
class Test01{
public static void main(String[] args) {
Test t = new Test();
t.name = "zhangsan";
t = null;//t地址为空,会报错
System.out.println(t.name);
}
}
//运行错误:NullPointerException:空指针异常


// 题二
public class Test {
public static void main(String[] args) {
Person p=new Person();
p.age=10;
add(p);
System.out.println(p.age);
}

public static void add(Person p) {
p.age++;
}
}

class Person {
int age;
}
// 最后输出什么???11



// 题三
public class Test {
public static void main(String[] args) {
Student s=new Student();
s.age = 18;
}
}

class Student {
String name;
int age;
public Student(int a){
age=a;
}
}

// 运行错误,没有无参构造器

封装(private)

  • 概念
    把类的内部细节(属性,方法)进行隐藏,对外提供公开访问的方法让其他类进行访问,此种方式为封装。

  • 作用
    可以防止恶意用户直接操作此属性或此方法,保证其安全性。

  • 步骤
    属性私有化 private
    书写读写器 getXXX() setXXX()
    在写器中设置判断语句

技巧:当set返回当前对象时,在使用时就可以进行连缀操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 日期类:
* 属性:年,月,日
* 封装属性,要求:年份在0以上,月份为12个月,日不能超过31天
*/
public class Date {
//编写实例变量
private int year;
private int month;
private int day;

public int getYear() {
return year;
}

public Date setYear(int year) {
//判断形参year的值是否在0以上,否则实例变量year的值为2021
if(year > 0){
this.year = year;
}else{
this.year = 2021;
}
/*
如果条件满足则执行if完成赋值,并且返回当前对象,否则执行else完成赋值并且返回当前对象
因此得到:无论条件是否满足都要执行返回当前对象
*/
return this;
}

public int getMonth() {
return month;
}

public Date setMonth(int month) {
//判断形参month的值是否在1---12范围内,满足赋值,否则将实例变量赋值为5
if(month >= 1 && month <= 12){
this.month = month;
}else{
this.month = 5;
}
return this;
}

public int getDay() {
return day;
}

public Date setDay(int day) {
//判断形参day的值是否在1---31范围内,否则实例变量day的值为24
if(day >= 1 && day <= 31){
this.day = day;
}else{
this.day = 24;
}
return this;
}
}



/*
测试类
*/
package demo1;
public class Test {
public static void main(String[] args) {
//创建日期对象
Date d = new Date();
//完成对实例变量赋值,则调用set方法完成
d.setYear(2009);
/*d.setMonth(71);
d.setDay(11);*/
/*
其实上面三条语句等价于如下一条语句
因此得到结论:
当set返回当前对象时,在使用时就可以进行连缀操作
*/
d.setYear(2009)
.setMonth(71)
.setDay(11);

//输出实例变量的值,则调用get方法完成
System.out.println(d.getYear() + "年" + d.getMonth() + "月" + d.getDay() + "日");
}
}

继承\泛化(extends)

发生在至少两个类
父类(别名:超类、基类、superclass):放进其他类的共同属性和行为
子类(别名:派生类、扩展类、subclass):放入其自己独有的属性和行为
遵循 is a原则,例如:cat is an Animal

特点:减少重复代码书写、耦合度高、单一继承

继承中,父类的私有属性不能被子类。
继承中,父类的构造方法不能被子类继承。

在Java中,Object类是所有类的父类

1
2
3
4
5
6
7
8
9
编写父类语法格式:
[修饰符] class 父类类名{
编写公共的属性和公共的方法
}

编写子类语法格式:
[修饰符] class 子类类名 extends 父类类名{
编写独有的属性和独有的方法
}

关键字:this与super

this

概念: 指代当前对象。是一个变量,是一个引用,且引用的是当前对象。

this代表的是对象本身(对象级别的);所以所以可以输出(对象地址),可以调用实例变量和方法,但存在于堆区、不能用在静态方法中

this在大部分情况下是可以省略的。

this可以在构造方法中使用,用来区分属性和局部变量(当属性和局部变量同名时)。

构造方法是可以在另外一个构造方法中调用
语法:

1
2
3
4
this(); //调用无参构造方法
this(参数列表); //调用带参构造方法

// 注意!!!写法必须是构造方法中的第一句话,并且只能用一次

super

概念: super是关键字,所代表的是来自于父类的那一部分特征。

super的语法:
super.属性: 调用父类的属性.
super.方法: 调用父类的方法.

super不是一个变量,不能输出。
super可以认为是this的一部分,但是它没有开辟任何的内存空间只负责调用来自父类的特征。

super关键字也可以省略。

super在构造方法中的使用
子类不管发生什么情况,一定会自动去调用父类的无参构造方法
父类的无参构造方法,把它显示地声明出来。

1
2
super();  调用父类的无参构造方法
super(参数列表);调用父类的带参构造方法

区别

this代表是当前对象,是一个变量,引用
super 不是一个变量,不会堆中开辟空间,super代表那个来自父类的那一部分特征
super是this的一部分

相同:
都可以调用属性,方法
都可以用在构造方法中。
都不能调用,用在静态方法。

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public static void main(String[] args) {
new B().show();
}
}
class A {
String name = "小明";
}

class B extends A {

String name="张三"; //可以允许子类定义与父类同名的属性
int age;
public void show(){

System.out.println(name);
System.out.println(this.name); //子类调用的是自己类中的name属性
System.out.println(super.name); //父类特征
}
}
//最后输出张三、张三、小明

多态

  • 概念
    同一种事物,由于条件不同所得到的结果也不同。(多种形态、状态)

  • 例子
    水。低温: 固态;常温: 液态;高温: 气态

  • 需要满足三个步骤:
    要有继承结构
    要有方法重写
    父类引用,子类对象

class Animal { }
class Cat extend Animal{ }
Animal animal=new Cat();

  • 项目中的应用场合,一般是两种
    • 作为方法的参数:父类引用做方法参数使用
      service(Friend friend);
      Friend friend=new ChineseFriend();
    • 作为方法返回类型使用:
      public Pet getPet(int typeId)
      Pet pet=getPet(1);

(静态绑定,编译阶段)编译器会发现animal的类型是Animal类型,所以编译器会把Animal中的属性或方法给绑定上,如果属性和方法绑定上了,编译就通过;如果没有绑定上,则编译会出错;

(动态绑定,运行阶段)接下来运行的时候,发现堆内存中实际存储的对象是Cat对象, 此时,就会把Cat类中相关属性或方法绑定上。

  • 语法
    向上转型 (类似于自动类型转换): 子类—>父类(低转高)
    Animal animal=new Dog();

    向下转型(类似于强制类型): 父类—>子类(高转低)
    有概率出错
    Dog dog=(Dog)animal;

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
public static void main(String[] args) {
Animal animal=new Animal() ;
Dog dog=new Dog();
Cat cat=new Cat();
dog=(Dog)animal;//这里
}
}
class Animal {
public void show() {}
}

class Cat extends Animal {
public void play(){ }
}

class Dog extends Animal {
public void swim() {
}
}

//运行错误,从父类引用animal转为子类Dog类型时,转换出错。

关键字:instanceof

用来判断变量名(父类)存放的是否是类名(子类)对象,如果存放在,则对象则结果为true,否则为false。

  • 语法:
    if (变量名 instanceof 类名或者接口){ }

抽象(abstract)

一般在类上声明,同时可以声明抽象方法。但不能修饰属性!!!

抽象类中可以定义非抽象属性,方法,也可以定义抽象方法
抽象类不能实例化 ,实例化此抽象类下的子类

抽象父类有一个方法,子类可以选择不实现或去实现。如果子类选择了不实现,子类自己声明成abstract类即可。如果子类要实现,子类应该把此方法打开,书写方法内容。

抽象类中是否一定定义抽象方法? 不一定
抽象方法是否一定存在于抽象类中? 是的。

  • 修饰类: 抽象类
    抽象类的意义从语法角度说,里面加入一个抽象的方法

    语法:

    1
    2
    3
    4
    5
    public abstract class A  {
    //属性
    int a=1;
    抽象类中可以加入实例方法,变量,静态方法,变量
    }
  • 修饰方法:抽象方法
    语法:
    public abstract void show() { }
    抽象类从实际应用来说,生活中是抽象的,代码也必须设计成抽象的形状
    abstract class Shape { }

接口(interface)

  • 概念:
    接口是一个特殊的抽象类
    里面只有2个内容:静态常量,抽象方法
  • 使用语法
    [访问修饰符] interface 接口名 { }

接口,可以看成定义为一种能力

接口不能实例化,需要实现类来实现
接口中的静态常量一般很少写,所以认为接口中就是抽象方法
抽象类和接口都不能创建对象,也就是说:抽象类和接口都是多态的一种形式

两个多
可以多实现
可以多继承

  1. 在JDK8.0中,接口中包含:抽象方法(默认有public abstract)、公有静态常量(默认有public static final)、public static修饰的方法、public default修饰的方法

  2. 关系
    在Java中,类与类之间是继承,也就是:子类继承父类,并且是单继承

    1
    2
    3
    4
    class A{
    }
    class B extends A{
    }

    在Java中,接口与接口之间是继承,也就是:子接口继承父接口,并且是多继承

    1
    2
    3
    4
    5
    6
    interface X{
    }
    interface Y{
    }
    interface Z extends X,Y{
    }

    在JAVA中,类与接口之间是实现,也就是:实现类实现接口,并且是多实现

    1
    2
    3
    4
    5
    6
    interface A1{
    }
    interface B1{
    }
    class C1 implements A1,B1{
    }

    接口不能继承类

  3. 在一个类中,该类既有继承也有实现,则继承必须位于实现的前面

    1
    2
    public class A extends B implements X,Y {
    }
  4. 在Java中,接口中的抽象方法默认有public abstract,可以改写成如下

    1
    2
    3
    public interface Pet{
    void eat();
    }
  5. 接口不能创建对象,也就是说:接口也是多态的一种形式
    比如:Pet p = new Dog();

  6. 接口也是一种数据类型,属于引用数据类型,在内存中存放的是地址(引用)

  7. 当实现类实现接口时,必须要重写接口中所有的抽象方法,否则实现类也是抽象类
    大多数情况下,实现类都要重写接口中的所有抽象方法

  • 接口和抽象类有和区别
    抽象类中可以有抽象方法,还有非抽象的属性和方法;接口只有抽象方法
    抽象类半抽象的;接口纯抽象。
    接口和抽象类都不能实例化
    抽象类只能实现单一继承,而接口多继承。

  • 面向接口编程程序设计的步骤:
    定义一个接口,书写抽象方法
    定义一个实现类,实现接口中的所有抽象方法
    可以使用接口多态:
    语法: 接口类型 接口名=new 实现类类名();

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Test01 {

public static String name="a"; //对的

int age=2; //对的

public int show(); //对的

void print(); //对的

public MyInterface(); //错的

private int sum(); //错的
}

Object中有常见方法

toString():把一个对象变成一个字符串
getClass():得到此对象所属类型
equals() : 判断2个对象是否相等

hashCode(): 得到对象哈希码值
finalize(): 垃圾回收器清理不用对象

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// equals() : 判断2个对象是否相等,重写equals
public class Test01 {
public static void main(String[] args) {
String s1="abc";
String s2="abc";
System.out.println(s1.equals(s2)); //true

Student s11=new Student("zhangsan",18);
Student s21=new Student("zhangsan",18);

System.out.println(s11.equals(s11)); //false
}
}



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

/**
* 重写equals()方法
*/
public boolean equals(Object obj) {
//判断传入的对象是否为空
if (obj==null){
return false;
}
if (!(obj instanceof Student)){
return false;
}
if (this==obj){
return true;
}
boolean isTrue;
//把传入的对象变成学生类对象
Student s=(Student)obj;
//重写思路:比较2个对象的内容是否相等
/**
if (this.name.equals(s.name)&&this.age==s.age){
isTrue=true;
}else {
isTrue=false;
}
return isTrue;
*/
//简化后如下
return this.name.equals(s.name)&&this.age==s.age;
}
}

内部类

概念: 在一个类中再编写一个类。里面的这个类,称为内部类,也叫做内置类或者叫做嵌套类

  • 成员内部类
    作为外部类的一个成员
  • 静态内部类
    此类为静态的
  • 局部内部
    在方法定义中的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyClass {
//称为成员变量
int a;
//称为成员方法,也可以叫做实例方法
public void print(){
Inner i = new Inner();
}
//称为内部类,也可以叫做成员内部类,一般是在外部类的实例方法中使用
class Inner{

}

//称为成员方法,也叫做静态方法
public static void show(){
Inner2 i = new Inner2();
}
//在内部类前面编写static关键字,称为静态内部类,一般在外部类的静态方法中使用
static class Inner2{

}

public void print3(){
int num; //局部变量
class Inner3{ //称为局部内部类

}
}
}
  • 匿名内部类(重点)
    代表此类没有名字。 是一种特殊的局部内部类。

当只创建一次对象时,可以使用匿名内部类完成。
当使用匿名内部类前提,必须要继承父类或者实现接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
语法: 
new 要实现的接口名称()/要继承的类名() {
接口中的方法()/父类的方法()实现
}


// 例子一
// 接口
public interface Pet {
void eat(); //编写吃的抽象方法
}

// 测试类
Pet p1 = new Pet(){

public void eat() {
System.out.println("正在吃Xxxxxx....");
}
};
p1.eat();

// 上下两个等价

//其实只创建一次对象时, 还可以使用匿名内部类完成
new Pet(){
public void eat() {
System.out.println("正在吃Xxxxxx....");
}
}.eat();

类与类之间的关系

  1. 继承(泛化)
    子类继承父类,子接口继承父接口
  2. 实现
    实现类实现接口
  3. 依赖
    如果A类中方法的返回值类型或者参数列表或者局部变量使用到了B类,则称为A类依赖于B类
  4. 关联
    如果A类中使用B类定义了成员变量,则称为A类关联B类
  5. 聚合
    聚合也是关联的一种,如果A类中包含若干个B类,但是A类不能限定B类的生命周期,称为A类为聚合类
  6. 组合
    组合也是关联的一种,如果A类中包含若干个B类,但是A类能限定B类的生命周期,称为A类为组合类

异常

  • 概念:
    一段程序代码在执行过程中,遇到了不正常的事件,发生了中断,这种方式为异常。

  • 异常有分类: (应用级别)

    • 检查异常(checked异常)
      编译时,会检查此异常,必须先去解决,否则编译不通过
    • 非检查异常(unchecked异常: )
      编译正确,不报错,但是运行时会引发此异常

抛出异常遵循的是反向链的调用方式

  • 异常结构图: api类库结构图
    根类: Throwable
    子类: Error(系统级) Exception(应用级)
    常见的应用级(应用级): NullPointerException ClassCastException, ArithemeticException,InputMismatchException,ArrayIndexOutOfBoundsException …….

  • Java异常处理机制
    try: 尝试
    catch: 捕获
    finally: 最终,用来释放资源 ,关闭
    throw: 抛: 语句级
    throws:方法定义抛出异常

throw和throws

在实际工作项目中,建议throw和throws两个关键字同时书写,避免出现异常无法处理

  • throw: 语句抛异常
    语法: throw 要抛出的异常对象;
    举例:throw new RuntimeException();

throw用在方法内部语句抛出异常

1
2
3
public void show(){
throw new RuntimeException(); //语句级异常
}
  • throws: 方法抛异常
    语法: throws 要抛出的异常类名
    举例: throws RuntimeException

throws关键字写在方法定义的小括号的后面

1
2
3
public void show()throws 要抛出的异常类名{

}

try…catch…finally

try必须,catch,finally不一定具备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 捕获多个异常,多重catch选择结构。(类似if() else if())
// 注意:书写异常的顺序按照有小到大的顺序
//catch(异常类1|异常类2 引用) //jdk1.5新特性

try {

}catch(){

}catch(){

}catch(){

}
......





// 完整语法
try {
//要监控的代码
}catch(要捕获的异常类名 此异常类名产生的引用(变量)) {
//捕获异常后要做的事情
//以下为常用错误
//设计异常输出内容: System.err.println("这写自定义的东西");
//打印异常的消息: System.out.println(引用.getMessage());
//打印异常跟踪信息: 引用.printStackTrace();

}finally {
//要释放的资源,要关闭的内容
//数据库用完后的完毕
}

自己写异常类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
年龄设置0至100岁,否则异常输出
*/
//测试类
public class TestAges {
public static void main(String[] args) {
Ages ages = new Ages();

try {
ages.setAge(200);
}catch (AgesException e){
System.err.println("输出红色字体");
e.printStackTrace();
}
System.out.println("继续运行");
}
}

//年龄类
public class Ages {
private int age;

public void setAge(int age) throws AgesException {
if (age>= 0 && age<= 100) {
this.age = age;
}else {
throw new AgesException("抛出异常");
}
}
}

//自定义异常类
public class AgesException extends Exception {
public AgesException(String message){
super(message);
}
}

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// 题目一
public static void main(String[] args) {
try {
int a=10;
int b=0;
int c=a/b;
}catch(Exception e) {
System.out.println("出错了!");

}catch(ArithemeticException e) {
System.out.println("出错了2!");

}catch(InputMismatchException e) {
System.out.println("出错了3!");
}
}
// 多重catch有顺序 ...由小到大。



// 题目二
try {
int a=10;
int b=0;
int c=a/b;

}catch(Exception e) {
return;
e.printStackTrace();
System.err.println("异常发生"); //运行出错,return后续代码不会执行

}finally {
System.out.println("代码结束");
}


//题目三
class Super {
public void m1() throws IOException {
throw new IOException();
}
}

public class Sub extends Super{
public void m1() {
}
}
//子类抛出异常必须比父类抛出的异常要小或者是没有。


//题目四
public class Test01 {
public static void main(String[] args) {
try {
Test02.add(0);
System.out.println("异常发生了");

}catch(Exception e) {
e.printStackTrace();
System.err.println("处理异常");

}finally {
System.out.println("处理结束");
}
}
}

class Test02 {
public static void add(int i) throws RuntimeException{
if (i==0){
throw new RuntimeException();
}
System.out.println("此处异常");
i++;
}
}
//最后输出:处理异常 异常结束



//题目四
public class Test01 {
public static void main(String[] args) {
int s=getValue(10);
}

public static int getValue(int i) {
try {
return i; //编译出错,方法最后没有返回值
} catch (Exception e) {
} finally {
i++;
}
}
}

数组

  • 概念
    是引用数据类型,类似一个盒子,可以存放多个数据,并且相同的数据类型、固定的空间大小。

  • 特点
    固定的空间大小,一次可以开辟一块连续的区域。
    每个空间有标识符下标,作用是根据下标,找到此空间中的数据
    空间大小是固定的,但可以扩充的。
    数组是引用数据类型
    数组中放入的数据可以是基本数据类型,也可以是引用数据类型 。

  • 数组的4大要素
    数组的数据类型
    数组名
    数组的长度
    下标

  • 优缺点
    查找效率高。有连续的空间、空间大小相同、有下标、首个空间的地址就是数组地址,通过偏移量和表达式运算计算出元素所在内存地址位置
    插入或删除效率比较低。会导致其他元素发生移位。

数组如果没有赋值,则会有默认值出现。

一维数组

  • 动态声明(声明时,只开空间不赋值)
    数据类型[] 数组名=new 数据类型[数组大小];
    Int[] a = new int[3];

    数据类型[] 数组名=new 数据类型[]{元素1,元素2,……}
    int[] a = new int[] {1,2,3};

  • 静态声明(声明时,一并赋值)
    数据类型[] 数组名={元素1,元素2,……};
    Int[] a = {1,2,3}
    数组名[下标]=值;

  • 扩充

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // 一对一扩充
    int[] a={1,2,3};
    int[] b=new int[6];
    for(int i=0;i<a.length;i++) {
    b[i]=a[i];
    }
    a=b;// b指向a,数组扩充完毕



    //arraycopy方法扩充
    //语法:System.arraycopy(原数组,哪里开始,新数组,新数组的开始,新数组的结束);
    // 把ints列表的前三,复制到ints2空列表的前三上

    public static void main(String[] args) {
    int[] ints = {11, 22, 33, 44, 55, 66};
    int[] ints2 = new int[10];
    System.arraycopy(ints, 0, ints2, 0, 3);





    // System.arraycopy(src ,srcPos ,dest ,destPos ,length); `
    // 可以把src数组中从srcPos开始的length个元素复制到dest数组从destPos开始的位置上
    // 把ints列表的前三,复制到ints2空列表的前三上

    public static void main(String[] args) {
    int[] ints = {11, 22, 33, 44, 55, 66};
    int[] ints2 = new int[10];
    System.arraycopy(ints, 0, ints2, 0, 3);

    for (int i : ints2) {
    System.out.println(i);
    }
    }

二维数组

  • 概念:
    二维数组是特殊的一维数组,是一维数组的嵌套。

  • 静态声明
    数据类型[][] 二维数组名={二维数组中的元素1,元素2}
    这里的二维数组元素是指一维数组
    int[] a={};

  • 动态声明
    数据类型[][] 二维数组名=new int [长度][一堆数组长度];

    数据类型[二维数组的总长度][二维数组中每一个一维数组的长度]
    int[] a = new int[2][3];

1
2
3
4
5
6
7
8
9
// 二维数组遍历(类似九九乘法表)
public static void show(int[][] array) {
for(int i=0;i<array.length;i++) {
for(int j=0;j<array[i].length;j++) {
System.out.print(array[i][j]+"\t");
}
System.out.println();
}
}

可变长参数

  • 概念
    可变长参数用于接收任意个数据, 即该参数接收数据的数量是可以变化的。

  • 语法
    修饰符 返回值类型 方法名( 参数类型 参数名, 参数类型… 参数名){ }

  • 说明
    一个方法最多只能有一个变长参数, 并且变长参数只能放在参数列表的最后
    在参数类型与参数名之间使用三个小点表示变长参数
    在调用方法时,可以给变长参数传递任意个数据
    在方法体中可以把变长参数当作数组使用

  • 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Test06 {
    public static void main(String[] args) {
    //在调用方法时, 可以给变长参数传递任意个数据
    sum();
    sum(1);
    sum(1, 2, 3, 4, 5);
    sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    int[] ints = {10, 20, 30};
    sum(ints); //也可以给变长参数传递一个存储整数的数组
    }

    //定义方法,计算 若干整数 的和, 就可以使用变长参数来接收这些整数
    public static void sum( int... data) {
    //在方法体中可以把变长参数当作数组来用
    int sum = 0;
    for (int i = 0; i < data.length; i++) {
    sum += data[i];
    }
    System.out.println("sum = " + sum);
    }

    }

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int[][] bb=new int[2][];  对的 :二维数组长度必须先声明   
int[][] cc=new int[][3]; 错的:没有二维数组总长度。

// 题目一
class Test01{
public static void main(String[] args) {
int[] a;
a={1,2,3}; //编译出错,出错原因就是此为静态声明,声明和赋值同时进行,不能分开。

int[] b;
b=new int[2]; //正确的,因为动态声明不要求声明时给值,可以先定义,然后再开空间最后再给值。

int[] aa=new int[3]; //正确的

int[] bb=new int[3]{1,2,3}; //错误的

int cc={1,23,3};
}
}



// 题目二
int[][] aa=new int[][]{{1,2},{3,4}}; //这是二位数组的动态赋值

int[][] bb=new int[2][]; //对的:二维数组长度必须先声明

int[][] cc=new int[][3]; //错的:没有二维数组总长度

foreach循环

快捷键:iter回车
使用foreach循环,增强的for循环遍历数组中的元素,格式为

1
2
3
for( 数据类型 变量名  :  数组名 ){
循环体
}

说明:
数据类型就是数组中元素的类型,如果数组存储int整数则数据类型就是int, 如果数组中存储double小数则数据类型就是double, 如果数组中存储String字符串则数据类型就是String
变量名由程序员自己命名, 只在当前foreach循环中有效
执行过程, 会把数组的每个元素依次赋值给变量,再执行循环体
foreach循环与for循环都可以遍历访问数组的所有元素,但是foreach循环仅用于遍历访问, for循环不仅可以读数组元素,还可以修改数组元素

1
2
3
4
5
6
// 例子
int [] ints = {1,2,3}; // 简化版赋值

for (int i : ints) {
System.out.println(i);
}

十大经典排序算法

https://www.runoob.com/w3cnote/ten-sorting-algorithm.html

冒泡排序思路:从前向后两两比较, 把大的数交换到后面就实现了由小到大的排序
选择排序思路: 从当前数组元素中选择最小的交换到前面

二分查找, 前提是数组已经实现了由小到大的排序, 始终与中间的元素比较大小, 如果比中间元素小,把范围缩小到左一半, 如果比中间元素大就把范围缩小到右一半

常用方法

字符串类

  • 概念
    String是一个引用数据类型,代表字符串,符号是一对双引号。并且是一个常量,此常量位于常量池。

实际项目中字符串拼接不要用加号,因为会大量产生常量并且占空间。

String 字符串名=”字符串内容”;
举例: String str="hello";

String 字符串名=new String(“字符串内容”);
举例:String str=new String("world");

字节数组—>字符串

  • 语法
    String(字节数组byte[] ,开始,结束);
1
2
3
4
5
6
7
byte[] bytes = {65,66,67,68,69,70,71,72,73,74};

String s2 = new String(bytes,3,5);
System.out.println(s2); // DEFGH

String s3 = new String(bytes);
System.out.println(s3); //ABCDEFGHIJ

字符数组—>字符串

在实际开发中,可能调用某个方法获得一个字符数组, 可以把字符数组中的字符连接为字符串

1
2
3
4
5
6
7
char [] chars = {'A','b','6','*','汉','字',97, 98, 30686, 30988};

String s = new String(chars);//把chars数组中所有的字符连接为字符串
System.out.println(s); // Ab6*汉字ab矞礌

String s2 = new String(chars,0,5);
System.out.println(s2); // Ab6*汉 把chars数组中从0开始 的5个字符连接为字符串

索引值 和 符串的长度

标获取此下标对应的字符
String.charAt(下标)

返回字符串的长度
String.length()

1
2
3
4
5
6
7
8
String s = "hello进阶";

System.out.println(s.charAt(0)); //h 显示第 0 个索引值

for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i) + " ");
}
// h e l l o 进 阶

比较大小

比较当前字符串与参数字符串的大小(ASCII码顺序),如果当前字符串大返回正数, 参数字符串大返回负数,相等返回0
compareTo(String anotherString)

1
2
3
System.out.println("hello".compareTo("你好啊啊啊")); // -20216 返回负数说明 compareTo()里的数大
System.out.println( "hello".compareTo("hello")); //0 返回0,相等
System.out.println("你好啊啊啊".compareTo("hello")); // 20216 返回正数说明 compareTo()里的数小

忽略大小写后再比较两个字符串的大小
compareToIgnoreCase(String str)
String类定义时,实现了Comparable接口,重写它的抽象方法compareTo()方法, 逐个比较字符串的每个字符,遇到第一个不相等的字符,码值相减

1
System.out.println("hello".compareToIgnoreCase("HELLO")); // 0 返回0,相等

是否包含XXX

返回的是布尔值

1
2
3
4
5
6
7
String s = "hello进阶";

System.out.println(s.contains("hello")); //true ,s字符串包含Hello
System.out.println(s.startsWith("hello")); //true, s字符串以hello开始
System.out.println(s.endsWith("hello")); //false, s字符串不是以hello结尾
System.out.println(s.equals("hello进阶")); //true s字符完全相等
System.out.println(s.equalsIgnoreCase("Hello进阶")); //true s字符忽略大小写,完全相等

编码转换

返回字符串在当前默认编码下对应的字节数组
byte[] getBytes()

1
2
3
4
5
//当前编码是UTF-8
String s = "hello进阶";

byte[] bytes = s.getBytes();//返回s字符串在当前默认编码(UTF-8)下对应的字节数组, 在UTF-8编码中, 一个英文字符占1字节, 一个汉字占3字节
System.out.println(Arrays.toString(bytes));//[104, 101, 108, 108, 111, -24, -65, -101, -23, -104, -74]

返回当前字符串在指定的charsetName编码下对应的字节数组
byte[] getBytes(String charsetName)

1
2
3
4
5
6
7
8
9
10
11
//当前编码是UTF-8
//把字符串转换为指定GB2312编码下对应的字节数组 !!如果显示错误需要在main方法说明以下

String s = "hello进阶";

byte[] gb2312s = s.getBytes("GB2312"); //GB2312是一种汉字编码,在这种编码中,一个英文 占1字节, 一个汉字占2字节,
System.out.println( Arrays.toString(gb2312s )); //[72, 101, 108, 108, 111, -74, -81, -63, -90, -67, -38, -75, -29]

//同样可以把gbks字节数组中的字节,再以GB2312编码解析为字符串
String s1 = new String(gb2312s,"GB2312");
System.out.println(s1);// hello进阶

返回索引值

返回str在当前字符串中第一次出现的索引值
int indexOf(String str)

返回str在当前字符串中最后一次出现的索引值
int lastIndexOf(String str)

返回当前字符串中从beginIndex开始 的字符串
String substring(int beginIndex)

返回当前字符串中[beginInex, endIndex)范围内的字符串
String substring(int beginIndex, int endIndex)

1
2
3
4
5
6
7
8
9
10
String path = "C:\\Users\\HuLu\\Desktop\\JavaProjects\\JavaSE\\src\\Test.java";

int slash = path.lastIndexOf("\\"); // 45 最后一个 \\ 在第45
System.out.println(slash);

int dot = path.indexOf("."); // 50 第一次出现 . 是在第50
System.out.println(dot);

String filename = path.substring(slash + 1 ,dot); // Test 返回当前字符串中(46,50]范围内的字符串
System.out.println(filename);

字符串—>符数组

1
2
3
4
5
6
7
8
String s = "hello进阶";

char[] chars = s.toCharArray();

for (char c : chars) {
System.out.print(c + " ");
}
// h e l l o 进 阶

大小写转换

把字符串中的大写字母转换为小写,返回新的字符串,原来的字符串不变
String toLowerCase()

把字符串中的小写字母转换为大写,返回新的字符串,原来的字符串不变
String toUpperCase()

1
2
3
4
5
6
s1 = "Good Good Study";
String lowerCase = s1.toLowerCase();
String upperCase = s1.toUpperCase();
System.out.println( lowerCase ); //good good study
System.out.println( upperCase ); //GOOD GOOD STUDY
System.out.println( s1 ); //Good Good Study

去掉前后的空白符

去掉前后的空白符, 返回新的字符串, 原来字符串不变
String trim()

1
2
3
4
s1 = "     Hello   world         ";
String trim = s1.trim();
System.out.println("**" + trim + "**"); //**Hello world**
System.out.println("**" + s1 + "**"); //** Hello world **

类型的数据转换

把其他类型的数据转换为String字符串
static String valueOf(int i)

1
2
3
4
5
6
7
8
int num = 456;
String s1 = String.valueOf(num);//把整数转换为字符串
System.out.println(s1); //456

String s2 = "789";
int num2 = Integer.parseInt(s2);//可以把数字字符串转换为整数

System.out.println(num2); //789

与正则匹配

String类中与正则表达式相关的几个方法
判断字符串是否匹配regex正则表达式
boolean matches(String regex)

1
2
3
4
5
6
7
Scanner sc = new Scanner(System.in);
System.out.println("请输入手机号");
String phoneNum = sc.next();
//需要先定义手机号的模式串, 手机号第一位是1, 第二位可以是[3456789],其他还有9位数字
String pattern = "1[3-9]\\d{9}";
//打印手机号是否匹配模式
System.out.println( phoneNum.matches(pattern) ); //匹配返回true,否则返回false

会把字符串中符合regex正则表达式的字符串使用replacement替换,返回替换后的字符串
String replaceAll(String regex, String replacement)

1
2
3
4
// 把数值变成*
String text = "java123456peng";
String s = text.replaceAll("\\d", "*");
System.out.println( s ); //java******peng

对当前字符串使用符合regex正则表达式的字符串进行拆分
String[] split(String regex)

1
2
3
4
5
6
String text = "A small step forward,A big step of civilization";
//把字符串中的单词拆分出来, 发现单词与单词之间使用空格或者逗号分隔的, 就对字符串使用[ ,]拆分 ,
String[] split = text.split("[ ,]"); //方法返回拆分后小的字符串组成的数组
for (String s1 : split) {
System.out.print(s1 ); //AsmallstepforwardAbigstepofcivilization (println可以给开行这里只为演示方便
}

可变字符串

字符串字面量与String是不可变性的这里不再赘述

StringBufferStringBuilder字符串缓冲区的意思。

  • 工作原理
    预先在内存中申请一块空间,存放字符序列。 如果空间满了,会重新改变缓冲区的大小,以容纳更多的字符序列。

  • 优势
    空间只开一块,容量可以扩充,大大减少内存空间的浪费。

  • 使用语法:
    StringBuffer 名称=new StringBuffer(); //产生一个StringBuffer对象 ,缓冲区的初始容量是16个字符
    StringBuffer sb3=new StringBuffer("hello"); //产生一个StringBuffer对象,里面放入了hello的字符串,容量是字符串长度+StringBuffer的初始容量16个字符

  • 不同点
    StringBuffer是一个线程安全的(支持同步)
    StringBuilder是线程不安全的(不支持同步)
    其他用法全部一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //1) 先创建StringBuilder对象
    StringBuilder sb = new StringBuilder();
    System.out.println( sb );

    //2) 字符串连接
    for (int i = 0; i < 10; i++) {
    //把数字连接到sb字符串中
    sb.append(i);
    }
    System.out.println( sb ); //0123456789

    //3) 删除
    sb.delete(3, 6); //删除sb字符串中索引值[3, 6 )范围内的字符
    System.out.println( sb ); //0126789

    //4)插入
    sb.insert(3, "hello");
    System.out.println( sb ); //012hello6789

    //5) 替换
    sb.replace(3, 8, "world");
    System.out.println(sb ); //012world6789

    //6) 逆序
    StringBuffer s = new StringBuffer("helloworld");
    s.reverse();
    System.out.println(s); //dlrowolleh

常用数学类

java.lang.Math数学类

1
2
3
4
5
6
7
8
9
10
11
12
//生成[0,1)范围内的随机小数
System.out.println( Math.random());

//Math.min()返回两个数中的较少者, Math.max()返回两个数中的较大者
System.out.println( Math.max(10, 20)); //20
System.out.println(Math.min(3.14, 5.89)); //3.14

System.out.println( Math.pow(2, 10)); //1024 2的10次方
System.out.println( Math.sqrt(100)); //10.0 平方根

System.out.println(Math.ceil(3.11)); //4.0 始终向前进一位的最小整数
System.out.println(Math.floor(3.99)); //3.0 始终不进位进一位最大整数

专门生成随机数

在java.util.Random类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//先创建Random对象
Random random = new Random();

//生成[0, 1) 范围内的随机小数
for (int i = 0; i < 10; i++) {
System.out.println( random.nextDouble());
}

for (int i = 0; i < 10; i++) {
System.out.println( random.nextInt());
}

//生成[ 0 , limit) 范围内的随机整数
for (int i = 0; i < 10; i++) {
System.out.println( random.nextInt( 100 ));

数字格式化

java.text.DecimalFormat类中

把数字转换 为指定格式的字符串
可以在构造方法中指定模式串, 格式符有:
0 数字, 不足的位数会补0
# 数字

1
2
3
4
5
6
7
DecimalFormat df = new DecimalFormat("###,###.0000");
//调用format()方法可以把数字转换为 指定的格式
String s = df.format(1234.56);
System.out.println( s ); //1,234.5600

s = df.format(456789456.78999999);
System.out.println( s ); //456,789,456.7900

高精度(了解即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BigInteger i1 = new BigInteger("798465884651866515461651326513265132645485616541248651486516845141651326513");
BigInteger i2 = new BigInteger("798465864548561654126541651326513");

//add()相加, subtract()相减, multiply()相乘, divide()相除
BigInteger i3 = i1.add(i2);
System.out.println(i3);

i3 = i1.subtract(i2);
System.out.println(i3);

i3 = i1.multiply(i2);
System.out.println( i3);

i3 = i1.divide(i2);
System.out.println( i3 );

//注意:小数相除可能会遇到除不断的情况, 会抛出 ArithmeticException 算术异常
BigDecimal d1 = new BigDecimal("78894657986453.879465");
BigDecimal d2 = new BigDecimal("788946579863.8465");
//小数相除, 通常需要指定保留小数的位数,及尾数的处理方式
BigDecimal d3 = d1.divide(d2, 10, RoundingMode.DOWN);
//RoundingMode是一种枚举类型,枚举就是一组常量的组合
System.out.println( d3 );

日期类

Date日期类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//1) 创建Date对象返回当前日期
Date date = new Date();
System.out.println( date ); //Wed Aug 25 09:05:23 CST 2021

//2) 调用Date对象的getTime()返回 时间戳(毫秒级) 从1970-1-1 00:00:00开始
long millis = date.getTime();
System.out.println(millis); //1629853674892

//3) 获得当前日期对应的毫秒数 //系统级
long timeMillis = System.currentTimeMillis();
System.out.println( timeMillis );

//4) 根据毫秒数创建日期对象
Date date2 = new Date(timeMillis);
System.out.println( date2 );
date2 = new Date(1000);
System.out.println( date2 ); //Thu Jan 01 08:00:01 CST 1970, CST是中国时间,距离世界协调时有8小时的时差

//5) 格式化 Data ----> String 常用的格式符有: y年, M月, d日, H小时, m分钟 , s秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//把日期转换 为字符串
String s = sdf.format(date);
System.out.println( s ); //2021-06-01 15:33:57

//6) 解析 Sting ----> Data
String text = "2086年5月8日 9:28:58";
//根据字符串创建新的SimpleDateFormat对象, 或者重新设置SimpleDateFormat对象的格式
SimpleDateFormat another = new SimpleDateFormat("yyyy年M月d日 H:mm:ss");
sdf.applyPattern("yyyy年M月d日 H:mm:ss");
//调用SimpleDateFormat对象的parse(String text)可以把字符串转换为日期对象,
// 调用方法提示语法错误:Unhandled exception: java.text.ParseException未处理的异常, 当前Alt+Enter,选择Add exception to method signature抛出处理, 支行程序后,如果sdf的模式串与字符串不匹配就会产生异常
date2 = sdf.parse(text);
System.out.println( date2 );

Time日期类

java.util.Date日期类不是线程安全的, 在JDK8中新增了一组线程安全的日期API,在java.time包中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建LocalDateTime对象返回当前日期, LocalDateTime类的构造方法使用private修饰为私有的, 不能直接new对象了, LocalDateTime类提供一组静态方法返回该类的对象
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println( localDateTime ); //2021-06-01T15:43:59.291

//格式化 LocalDateTime对象 ----> String
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
//调用 LocalDateTime 对象的实例方法format( DatTimeFormatter )可以转换为字符串
String s = localDateTime.format(dtf);
System.out.println( s ); //2021年06月01日 15:46:30

//把字符串转换为LocalDateTime对象
String text = "2086年5月8日 9:28:58";
//根据字符串创建DateTimeFormatter
DateTimeFormatter another = DateTimeFormatter.ofPattern("yyyy年M月d日 H:mm:ss");
//调用 LocalDateTime类的静态方法parse(String, DateTimeFormatter)可以把字符串解析为日期, 如果字符串与模式串不匹配就会产生异常
LocalDateTime dateTime = LocalDateTime.parse(text, another);
System.out.println( dateTime);

Arrays类常用方法

Arrays.deepToString (二维数组)

可以把二维数组 元素连接为字符串

1
2
3
4
int [][] twoInts2 = new int[6][10]; 
System.out.println(Arrays.deepToString( twoInts2));

// [[0, 0], [0, 0], [0, 0]]

Arrays.toString( 数组名 )

可以把数组元素连接为字符串

1
2
3
4
int[] ints = {32, 54, 76, 98, 87, 54, 12, 5 };
System.out.println(Arrays.toString(ints));

// [32, 54, 76, 98, 87, 54, 12, 5]

copyOf( src源数组, 新数组的长度)

可以实现数组的复制, 会根据newLength新数组长度创建一个新的数组, 把src源数组中的元素复制到新数组中, 返回新的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
//新数组长度可以比原来数组长度大,类似于数组扩容
int[] ints = {32, 54, 76, 98, 87, 54, 12, 5 };
int[] bigger = Arrays.copyOf(ints, ints.length * 2);
System.out.println( Arrays.toString(bigger));

//[32, 54, 76, 98, 87, 54, 12, 5, 0, 0, 0, 0, 0, 0, 0, 0]


//新数组长度也可以比原来数组长度小
int[] smaller = Arrays.copyOf(ints, ints.length / 2);
System.out.println( Arrays.toString(smaller));

//[32, 54, 76, 98]

Arrays.sort() 从小到大排序

调用Arrays.sort( 数组, from, to )方法可以只对象数组中[from, to)范围内的元素进行排序

1
2
3
4
int[] ints = {32, 54, 76, 98, 87, 54, 12, 5 };
Arrays.sort(ints);
System.out.println( Arrays.toString(ints));
//[5, 12, 32, 54, 54, 76, 87, 98]

Arrays.binarySearch() 二分查找

排序后可以二分查找。如果它包含在数组中,则返回搜索键的索引,返回负数说明不存在

1
2
3
System.out.println( Arrays.binarySearch(ints, 5));
System.out.println( Arrays.binarySearch(ints, 54));
System.out.println( Arrays.binarySearch(ints, 55)); //-6,负数说明不存在

枚举(enum)

本质就是类,只不过一次可以开多个对象
使用自定义枚举类型定义变量, 赋值枚举常量值时,在常量前要加枚举类型名的前缀
当变量的值是有限的几个离散常量值时可以定义为枚举类型, 如性别, 季节等 , 通过枚举可以提高程序的可读性与健壮性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Test {
public static void main(String[] args){
Result result = Result2.女;
System.out.println(result); //女

Result result2 = Result.SUCCESS;
System.out.println(result2); //SUCCESS
}
}

enum Result{ //使用enum关键字定义枚举类型, Result就是枚举类名
//在枚举类体中定义一组常量, 常量值之间使用逗号分隔
SUCCESS, FAILURE
}

enum Result2{
男,女
}



//可容纳的
enum Season {
枚举对象() {
实现的是抽象方法的内容。
}
构造方法()
实例方法()
静态方法()
抽象方法(); //可以加入,但是必须去实现,每个枚举对象都必须去实现。
枚举不能有逻辑判断如if
}

创建包装类对象

  • 概念
    每一个基本类型都有一个包装类,对基本类型的包装。
  • 目的
    让基本类型变成对象类型,可以使用属性和方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
基本类型                包装类 
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

//1) 所有的包装类都可以根据 基本类型数据创建
Integer i1 = new Integer(123);
Double d1 = new Double(3.14);
Boolean b1 = new Boolean(true);
Character c1 = new Character('汉');

///注意
Byte bb = new Byte((byte)66);

// 除了Character外,其他的包装类可以根据String字符串创建

包装类常用方法(以Integer为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Integer i1 = new Integer(123);
Integer i2 = new Integer("456");


/*
Integer ---> byte
对象-->基本: 拆箱操作
*/
byte bb = i1.byteValue(); //就是把i1的value属性值 123 强制转换为byte字节类型。

/*2)
基本类型比较大小

static int compare(int x, int y) 比较两个int基本类型数据的大小,
如果第一个数大返回正数, 相等返回0, 第二个数大返回负数
*/
System.out.println( Integer.compare(10, 20)); //-1
System.out.println( Double.compare(45.2, 3.9)); //1
System.out.println( Boolean.compare(false, false)); //0
System.out.println( Boolean.compare(true, false)); //1

/*3)
包装类对象比较大小

int compareTo(Integer anotherInteger) 比较两个包装类对象的大小,
如果当前对象大返回正数,相等返回0,参数对象大返回负数
所有的包装类都实现了Comparable接口, 重写了compareTo()方法.
包装类对象比较大小时,其实比较的是包装类对象的value属性值的大小
*/
System.out.println( i1.compareTo(i2)); //-1

/*4)
数字字符串 ---> int整数

static int parseInt(String s) 可以把数字字符串转换为int整数
*/
int num = Integer.parseInt("789");
double dd = Double.parseDouble("12.45");
boolean fff = Boolean.parseBoolean("kdkd");
///注意Character没有parseCharacter()方法,不能把字符串转换为char字符
//把基本类型转换为字符串
String s = String.valueOf(num);
s = "" + num;

/*5) 基本-->对象: 装箱操作 兼容类型

static Integer valueOf(int i)
static Integer valueOf(String s)
从JDK9开始 , 建议调用静态方法valueOf()创建包装类对象
*/
i1 = Integer.valueOf(258);
i2 = Integer.valueOf("369");
// Double.valueOf()
// Boolean.valueOf()
// Character.valueOf()

装箱与拆箱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// jdk1.5之后:jdk新特性
//自动装箱
// 从int到Integer过程:基本-->对象
Integer i1 = 789; //程序运行这一行时, 根据基本类型数据789创建一个Integer对象, 把这个对象的引用现付给i1

//自动拆箱
// 从Integer到int过程:对象-->基本
int x = i1; //系统会自动 的把i1包装类对象的value属性值赋值给x



Integer i2 = 789; //根据789创建一个新的Integer对象,把这个对象的引用赋值给i2
System.out.println( i1 == i2 ); //false

Integer i3 = 69;
Integer i4 = 69;
System.out.println( i3 == i4 ); //true
/*
java认为 -128~127 范围内的整数使用最频繁, 所以这个范围内的整数自动装箱后保存到共享池中
即i3与i4引用了共享池中同一个Integer对象.
类似于字符串字面量, 都采用享元模式, 为了减少对象创建的数量
*/

Long gg1 = 12L;
Long gg2 = 12L;
System.out.println( gg1 == gg2 ); //true

集合

  • 概念
    是一个容器,大小是可以自动扩展的,里面放入的数据全是对象
  • 分类
    单值集合: 此集合中只放入一个值。如:list,set
    键值配对集合: 也叫key-value集合,放入2个部分,一个key,一个value. 比如:map

关系图
关系图

Collection接口

  • 常用方法

    1. add(Object o) : 添加元素
    2. remove(Object o): 删除元素
    3. size(): 大小
    4. clear(): 清空元素
    5. isEmpty(): 判断集合是否为空
    6. toArray():把集合转为数组
    7. contains(Object o):包含指定元素
    8. iterator(): 迭代方法
  • 迭代器遍历步骤
    产生集合对象,调用iterator()方法,得到迭代器对象,返回得到是布尔值
    提供迭代器对象,不断调用hasNext()方法,看是否有下一个元素
    如果有数据,则通过迭代器对象,调用next()方法,取出下一个数据

  • 迭代器的原理:
    迭代器中有一个指针,一开始指到空挡位置,并没有指向实际数据。而当调用hasNext()方法后,指针下移,从下一个位置取出这个集合中对应的快照。(如果“快照”被破坏就报错)

List

  • 语法
    List 集合接口名=new 实现类名();

  • 特点
    有序 、可重复

  • 常见独有方法
    add(int index,Object o): 在指定的位置添加元素
    get(int index): 根据下标获取该下标对应的元素

  • 排序
    有一个对集合进行操作的工具类:
    Collections.sort(要排序的list集合);

  • LinkedList

    • 概念
      按照顺序从头依次接下来。
    • 链表的基本单元是节点。(Node)
      每一个节点中有2个部分
      数据、下一个节点的地址。头,尾节点没有地址。
  • 比较

    集合名 底层算法 查询速度 添加与删除
    ArrayList 数组 快,因为有一块连续空间、下标、地址,可以计算
    LinkedList 链表 慢,因为分散的内存空间

set

  • 特点
    无序、不可重复、没下标

  • 语法
    Set 集合名=new HashSet();

  • 常见方法
    大部分来自父接口Collection

  • HashSet消除重复的原则
    首先先去看每个对象的Hash码,调用一个hashCode(),看每个对象的hashCode是否相等,如果不相等,则HashSet认为这是不同的元素,则把它们全部加入集合;然后,如果Hashset值相同的,则会去调用equals()方法,看两个对象内容是否相等,如果内容不相等,则hashset认为是不同的元素,分别加入集合。最后,如果equals()方法比较内容相同,则认为是相同元素,则会消除重复。
    所以最好把HashCode(),equals()重写.

  • SortedSet(比较少用)
    特点:可以排序(底层是treeMap集合)
    使用: 和Set使用一样
    TreeSet消除重复和排序都是重写Comparable排序接口中的comareTo()方法做到的。和hashset中的hashCode(),equals()重写没有关系。

  • 排序
    Comparable:这种接口必须由一个类去实现,不然此接口中的方法无法实现 默认实现接口
    主要重写compareTo(Object o);

    Comparator: 这个接口比较灵活,分离比较规则,无需关联一个类,处理上更加人性化。

Map接口

  • 概念
    此集合中加入两个部分的值,一个是key,一个是value ,根据key找到value

  • 语法
    Map map引用=new HashMap();

  • 常见独有方法:
    put(Object key,Object value)添加元素
    get(Object key)根据键,得到值
    containsKey(Object key)判断集合中是否有指定的key存在

  • 应用场景
    集合在项目中一般用来临时存储数据的,首选是集合。如电商的购物车。

  • 特点
    无序的,如果键重复,会覆盖相同键中原来的值

  • HashMap算法
    红黑树: 二叉树结构(平衡)
    插入,删除元素都没有问题,当要插入或删除的元素比较多,出现插入或删除的效率会低下的问题。
    hashmap中元素不多时,一般使用链表结构,链表搜索很复杂。

  • 遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    map.put("111", "aaa");
    map.put("222", "bbb");
    map.put("333", "ccc");

    for (Object o : map.entrySet()) {
    Map.Entry entry = (Map.Entry)o;
    System.out.println(entry.getKey() + "\t" + entry.getValue());
    }

    //单独获取Key值
    Set set=map.keySet();
  • HashMap与Hashtable区别

集合名 特征
HashMap 键值都可以为空, ,轻量级,线程不安全(多人并发访问),访问速度快
Hashtable 键值都不能为空, 重量级,线程安全,访问的速度会慢

如果hashmap也要线程安全就需要声明
Collections.synchronizedMap(new HashMap());
排序工具类
Collections.sort();

  • TreeMap

    • 特点
      可以排序,但对key排序

    • 语法
      Map map=new TreeMap();

      使用了两种接口实现排序的
      Comparator: compare(Object o1,Object o2)
      Comparable: compareTo(Object o)

      实际工作中常用两种集合:ArrayList和HashMap.

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
创造迭代器
*/
public class 迭代器 {
public static void main(String[] args) {
Collection c=new ArrayList();

//2.集合添加元素
c.add("hello");
c.add("world");
c.add(1);

//3.遍历
//调用iterator()方法,返回迭代器接口
Iterator it=c.iterator();

//4.通过上面的迭代器接口,调用hasNext()方法
//循环查看迭代器中是否有下一个元素
while(it.hasNext()) {

//如果有,进入循环,取出下一个元素
//迭代器取数据
Object o=it.next();

//打印输出
System.out.println(o);
}
}
}



/*
迭代器遍历快照被破坏
*/
public class Test {
public static void main(String[] args) {

Collection c=new ArrayList();

c.add("hello");
c.add("ok1");
c.add("ok2");

Iterator it=c.iterator();
while(it.hasNext()) {
Object o=it.next();
c.remove("ok1");
System.out.println(o);
}
}
}

/*
遍历键值对
*/
map.put("111", "aaa");
map.put("222", "bbb");
map.put("333", "ccc");

for (Object o : map.entrySet()) {
Map.Entry entry = (Map.Entry)o;
System.out.println(entry.getKey() + "\t" + entry.getValue());
}

泛型

  • 概念
    只能往”盒子”中放入同种数据类型。规定了集合中存放元素的具体的类型。

  • 泛型能修饰
    集合,类,接口,方法。

  • 泛型的符号
    <>

  • 在集合中的语法:
    单值
    集合类型<集合中规定的数据类型> 集合名=new 实现类名<集合中规定的数据类型>();
    key-value
    集合类型<key的类型,值的类型> 集合名=new 实现类名<key的类型,值的类型>();

    例子

    1
    2
    List <String> list = new ArrayList <String>();
    Map <String,String> map = new HashMap <String,String> ();
  • 在类中

    1
    2
    3
    4
    实例化
    Box<String> box = new Box<String>();

    <String>后box只能输入str类型了

文件

  • 概念
    文件是把相关的一些记录放在一起,形成了数据,这些数据的集合为文件。

  • File类常见方法
    getName(): 得到文件名称
    length():长度
    getPath(): 获取文件绝对路径或相对路径
    exists(): 判断文件是否存在
    createNewFile(): 创建一个空的新文件
    lastModified():最后修改时间
    isDirectory()/isFile() : 是一个文件还是一个目录
    getAbsolutePath(): 获取文件绝对路径
    mkdir()/mkdirs(): 创建目录
    delete(): 删除文件
    getParent():得到文件的父目录
    list(): 列出当前目录下所有的内容

  • 例子:

    1
    2
    3
    File f = new File("c:/a/a.txt");

    f.mkdirs();

IO流

  • 概念
    是一个通道,是用来传输数据的,一连串数据,遵循一定的方向,采用是先进先出(FIFO)方式。


    方向

  • 归类

    • 按照流向划分:
      输入流和输出流

    • 按照数据单位划分:
      字节流:能解决所有的情况。
      字符流:解决字符问题。

    • 按用途划分:
      打印流、对象流。。。

字节流

FileInputStream/FileOutputStream
文件输入/输出流:是一个基本的节点流

  • 语法
    FileInputStream(File file)
    FileInputStream(String pathname)

  • 方法:
    read(): 读取一个数据,数据单位就是字节
    返回int类型,返回实际的数据,如果流中没有数据,则返回-1.
    close():关闭流
    flush()刷新流
    read(byte[] b): 把流中的数据读取到字节数组b,返回实际读取的长度

  • 文件输出流追加数据
    new FileOutputStream(路径,追加(true));
    String--->byte[]: 编码
    byte[]--->String: 解码

缓冲输入输出流

中间有个缓冲带。所以读完与写完必须要关闭流。
BufferedInputStream/BufferedOutputStream

对象流

对对象操作
ObjectInputStream/ObjectOutputStream
对象输入/输出流:

  • 特点
    流的数据类型是字节,把对象转成字节流。

  • 对象写到文件中,必须先序列化操作
    序列化: 把一个java对象转成字节的过程。
    反序列化:把字节转成java对象的过程

    如果不想读取到某个属性,可以加入关键字transient

  • 有关对象流的面试问题
    如果学生类中有一个属性它是另外一个类,请问在对象流中传输,会出问题。
    没有序列化,说明了类中的属性如果是另外一个类,此类也必须序列化

在属性中使用了关键字transient,这个会不会导致序列化终止?
序列化过程没有终止,但是属性不会通过流传递内容。

如果属性加入了static关键字,序列化是否终止?
序列化完成,但是结果也没有传递。

对于一个类,如果要实现序列化操作,建议在类中书写好此类的序列号。serialVerionUID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//对象输入流读取文件中的对象

public static void main(String[] args) {

//1.声明2个流
InputStream is=null;
ObjectInputStream ois=null;

//2.使用try...catch
try {
is=new FileInputStream("d:/a/a.txt");
//3.产生对象流
ois=new ObjectInputStream(is);

//4.读取对象
Student s1= (Student) ois.readObject();
Student s2= (Student) ois.readObject();

//5.学生数据打印
System.out.println(s1.getName()+" "+s1.getAge()+" "+s1.getHobby());
System.out.println(s2.getName()+" "+s2.getAge()+" "+s1.getHobby());

}catch (Exception e) {

e.printStackTrace();
}finally {

if(ois!=null) {

try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

// 学生类,实现序列化接口
public class Student implements Serializable {

private static final long serialVersionUID = -8355267483751747059L;
private String name;
private int age;
private String hobby;

字符流

  • 概念
    流动的是单个或多个字符。此流只能处理字符串,字符。但是不能处理文本之外的数据。

基本字符流(字节到字符的桥梁)
InputStreamReader/OutputStreamWriter

文件输入输出便捷类
FileReader/FileWriter
构造方法中,可以加入文件路径,可以实现追加。
缺点:不能处理字符集

最常用的是
BufferedReader/PrintWriter
readLine()以行为单位读取字符串
println()通过输出流把数据写入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//把studentDemo文本复制到student.txt下

public class Test {
public static void main(String[] args) {
PrintWriter pw = null;
BufferedReader br = null;

try {
//文本之间的转换
br = new BufferedReader(new InputStreamReader(new FileInputStream("c:\\a\\studentDemo.txt")));
pw = new PrintWriter(new FileOutputStream("c:\\a\\student.txt"));

//读取文件,写入文件然后刷新
String s=br.readLine();
pw.write(s);
pw.flush();


}catch (Exception e){
e.printStackTrace();
}finally {

//关闭文件
try {
br.close();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

其他流

PrintStream: 字节流
过滤流—>打印字节流

手工输入流: System.in
Scanner input=new Scanner(System.in);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 测试手工输入流:接收用户手工输入一系列字符遇到q停止
*/
public class Test07 {
public static void main(String[] args) {

//1.产生缓冲字符流
BufferedReader br=null;

//2.使用模板
try {

//产生对象 System.in
br=new BufferedReader(new InputStreamReader(System.in));
//给出提示
System.out.println("请输入:");
//定义一个字符串变量
String str=null;
//循环读取
while((str=br.readLine())!=null) {
System.out.println(str);
//判断一下:遇到了q停止
if("q".equals(str)) {
//退出
break;
}
}

}catch (Exception e) {
e.printStackTrace();
}finally {
if(br!=null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}



/*
PrintStream流
*/
public class Test06 {

public static void main(String[] args) throws FileNotFoundException {

//0.产生一个基本节点流对象
OutputStream os=new FileOutputStream("d:/a/a.txt");
//1.产生一个PrintStream对象
PrintStream ps=new PrintStream(os);

//改变控制台的输出方式
System.setOut(ps);

//测试
System.out.println("这是PrintStream的测试,输出内容待会到文件a.txt,你看不到的,哈哈!");

System.out.println("写入完毕!");

//关闭
ps.close();
}
}

多线程

  • 概念
    程序: 程序员编写的代码,那么可以运行。
    进程: 正在运行中的程序。一个程序对应着一个进程。
    线程: 是线程中的一个基本执行单元或执行场景,存在于进程中。一个进程包含至少一个线程,或多个线程。

大概关系图
内存图

  • 线程开发方式
    继承一个类: Thread类
    语法: class A extends Thread {}

    另外一种实现一个接口: Runnable接口
    语法: class B implements Runnable {}

    使用匿名类

    1
    2
    3
    语法: new 要实现的接口名称/要继承父类() {
    接口或父类中的方法(){}
    }
  • 状态

    • 初始状态
      new对象

    • 可运行状态
      调用start()

    • 运行状态
      抢到cpu

    • 结束状态
      运行结束

    • 阻塞状态: 当线程执行一些进入阻塞的方法后,那么会进入阻塞状态.
      sleep(): 睡眠方法(毫秒级别)
      这是一个静态方法,由Thread类来调用 Thread.sleep();
      join(): 加入方法
      如果在一个线程中加入了另外一个线程,则会让加入的线程先执行完毕,然后当前这个线程再继续接着执行。
      yield():让出机会
      如果有多个线程同时运行,可能会发生一个线程长时间占据了cpu,其他线程没有机会得到运行,为了防止此现象发生,采用了yield机制,把运行机会让出来,让其他线程能够得到运行。
      使用yield,线程优先级相同时,才能给其他线程能够得到运行的机会。

  • 设置优先级
    默认5,最低1,最高10
    t1.setPriority(Thread.MAX_PRIORITY); //最高优先级
    t2.setPriority(Thread.MIN_PRIORITY); //最低优先级

  • 注意
    获取线程名字: Thread.currentThread().getName();
    设置线程名字:Thread.currentThread().setName();

  • 线程的打扰、暂停方法:
    interrupt(),通过线程对象调用
    语法: 线程对象.interrupt();

线程同步

解决数据不一致问题

  • 概念
    多个线程相互争抢共同的资源(临界资源),会导致出现数据不一致,为了防止此现象,使用同步机制(锁机制)来保证数据的一致。

  • 三种锁

    方法锁:在方法加入synchronized关键字
    例子:

    1
    2
    3
    public synchronized static void show(){

    }

    对象锁 : 先产生一个锁,锁的类型是任意类型

    1
    2
    3
    4
    5
    6
    步骤: 
    1.先产生一把对象锁: Object obj=new Object();
    2.把对象锁放入synchronized() 中
    synchronized(obj) {
    要同步的代码
    }

    this锁

    1
    2
    3
    synchronized(this) {
    要锁的内容放在此处
    }

守护线程

  • 概念
    是一个线程,在后台默默地为其他线程提供服务。
    比如:JVM中,还有一个垃圾回收线程,属于守护线程,为主线程提供服务

  • 让t1线程成为守护线程
    Thread t1 = new TestThread();
    t1.setDaemon(true);

典型:守护进程Timer类

线程通讯机制

  • 概念
    有一方处于等待状态,在等待前的,必须去唤醒另外的处于等待的消费者或生产者线程。

生产者和消费者模型, 使用的是wait()/notifyAll()机制

  • 关于wait/notify的注意点
    此通讯机制必须使用同步
    wait/notify方法由对象调用,可以是任意对象,但是要确保调用这2个方法的对象,必须是同一对象
    调用notify()/notifyAll(),就会马上去唤醒那些处于等待状态的线程,让其马上工作。
    有条件地调用wait(),不然一直处于等待状态
    notify()/notifyAll()书写位置很灵活,它不限于现在前面还是后面

区别:调用wait()后,后台会释放对象锁;调用sleep()后,后台不会释放任何锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/**
* 题目一
*一共有200张票,反别由三个售票窗口卖票,票数依次递减
*/
public class Test03 {


public static void main(String[] args) {

//1.产生票
Ticket ticket=new Ticket(200);

//2.产生售票窗口
SellTicketThread t1=new SellTicketThread(ticket);
t1.setName("售票窗口1");
SellTicketThread t2=new SellTicketThread(ticket);
t2.setName("售票窗口2");
SellTicketThread t3=new SellTicketThread(ticket);
t3.setName("售票窗口3");


t1.start();
t2.start();
t3.start();
}
}

class Ticket {

//属性
private int num; //数量

public Ticket() {
}

public Ticket(int num) {
this.num = num;
}

public int getNum() {
return num;
}

public void setNum(int num) {
this.num = num;
}

/**
* 卖票
*/
public void sellTicket() {

//必须有票卖,卖多次,使用循环结构
while (true) {

//同步
synchronized (this) {

//设计退出循环的条件

if (this.num > 0) { //有票卖

//打印售票窗口的名字
System.out.println(Thread.currentThread().getName() + ":开始售卖第" + this.getNum());
//每卖出一张,就休息一下
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}

//数字得递减
this.num--;
} else { //无票了

//退出循环
break;
}

} //同步结束
//使用yield()
//给其他线程一个机会
Thread.yield();

} //while结束
}
}

/**
* 卖票线程
*/
class SellTicketThread extends Thread {

//属性
private Ticket ticket;

public SellTicketThread() {}

public SellTicketThread(Ticket ticket) {

this.ticket=ticket;
}

public void run() {

ticket.sellTicket();
}
}





/**
* 题目二:利用线程的通信机制,用两个线程打印以下的结果:
* 1 2 A 3 4 B 5 6 C 7 8 D ... 49 50 Y 51 52 Z
*/
public class Test01 {

public static void main(String[] args) {

//对象锁产生
Object o=new Object();
//2.线程对象产生
NumberThread t1=new NumberThread(o);
CharacterThread t2=new CharacterThread(o);

t1.start();
t2.start();


}

}

class NumberThread extends Thread {

//定义一个对象锁
private Object o;

public NumberThread(Object o){

this.o=o;
}

public void run() {

//同步
synchronized (o) {

for (int i = 1; i <= 52; i += 2) {

System.out.println(i); //1
System.out.println(i + 1); //2

//唤醒那个处于等待状态的

//让此线程陷入等待状态的字母线程
o.notifyAll();
try {
//有条件的wait()
if(i!=52)
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}
}

class CharacterThread extends Thread {

//产生对象锁
private Object o;

public CharacterThread(Object o) {

this.o=o;
}

public void run() {

//同步
synchronized (o) {

//循环打印
for(char c='A';c<='Z';c++) {

System.out.println(c);

//先去唤醒哪个处于等待状态的数字
o.notifyAll();

//字母陷入等待
try {
if(c!='Z')
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

新框架(了解)

jdk1.5版本后,推出了新的线程框架
线程池: ThreadPool
接口: ExecutorService

线程通讯机制更新:
wait(): await()
notify():signal()/signalAll()
synchronized(){} : lock取代

线程开发方式
实现一个接口: Callable接口
好处:能抛异常,方法有返回值。

DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* 线程池:属于jdk1.5之后的新线程框架
*/
public class Test01 {

public static void main(String[] args) {

//1.产生线程池
ExecutorService es=Executors.newFixedThreadPool(2);

//1.5产生Runnable对象
Runnable r1=new A1("*****");
Runnable r2=new A1("$$$$$");

//2调用submit方法,放入线程对象
es.submit(r1);
es.submit(r2);

//3.线程池用完后,要关闭
es.shutdown();

}
}

class A1 implements Runnable {

private String token; //符号

public A1(){}

public A1(String token) {

this.token=token;
}

@Override
public void run() {

for(int i=1;i<=100;i++) {

System.out.println(i+","+token);
}
}
}




/**
*
业务: 银行存取钱,要求:不断地存,存的钱不能高于2000元;
同时,取钱,不断地取,取的钱不能低于0 。
*/
public class Card {
private double balance; //余额

//做一个lock接口对象
Lock lock=new ReentrantLock();
//根据条件接口,调用newCondition()方法,得到条件接口对象
Condition c=lock.newCondition();


public Card() {}

public Card(double balance) {

this.balance=balance;
}

/**
* 存钱
*/
public void deposit(double money) throws InterruptedException {

// synchronized (this) {
//使用新现场框架的lock机制
lock.lock();

System.out.println("存钱线程:存入的钱是:" + money);

//不断地存
while (true) {
//存的钱余额不能高于2000
if (this.balance + money > 2000) {

//生产过多,暂停生产
//处于等待,目的让消费者前来消费
// this.wait();
c.await();
}else {

//小于2000,继续生产
//退出循环
break;

}
}
//加入钱
double now = this.balance + money;

//休息一下0.1秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//余额回传
this.balance = now;


//通知取钱线程过来取钱
//this.notifyAll();
c.signalAll();

//释放锁
lock.unlock();



//} //同步结束

System.out.println("存钱线程:当前的余额是:"+this.balance);
}

/**
* 取钱
*/
public void withdraw(double money) {

lock.lock();

//使用同步
// synchronized (this) {

System.out.println("取钱线程:取出的钱是:" + money);

//取钱,判断取的钱是否够
while (true) {

if (this.balance - money < 0) {

//等待
try {
//this.wait();
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

} else {

//有钱继续取
break;

}
}

//加入钱
double now = this.balance - money;

//休息一下0.2秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

//余额回传
this.balance = now;

//通知存钱线程,可以生产了
//this.notifyAll();
c.signalAll();

lock.unlock();

// } //同步结束

System.out.println("取钱线程:当前的余额是:"+this.balance);
}
}

反射

  • 概念
    就是操作字节码文件(class),目的是要获取字节码文件中的类的内部信息。如属性,方法,构造方法。而反射入口是Class对象。

  • 产生Class对象
    类名.class
    对象.getClass():
    Class.forName("类所在的位置");

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //获取Stu类对象的三种方法,都是输出 class Stu
    public class Test2 {
    public static void main(String[] args) {

    //你要获取哪个类的内部细节,类名就写谁
    //获取学生的反射入口
    //类类型 类对象
    Class c = Stu.class;
    System.out.println(c);

    //产生对象获取Class
    Stu s =new Stu();
    Class c2 =s.getClass();
    System.out.println(c2);

    //类所在的位置获取
    Class c3 = Class.forName("Stu");
    System.out.println(c3);
    }
    }

    class Stu {

    public String name;
    public int age;
    }
  • 属性:(Field)

    1. 得到所有属性
      getFields(): 得到本类中所有的公开属性
      getDeclaredFields():得到本类中所有的属性

    2. 得到单个属性
      getField(): 得到公开属性
      getDeclaredField(): 得到所有属性
      set(Object obj,Object value): 给属性设置值 传入实际对象,实际的值
      get(Object obj): 得到属性的值

      注意:对于私有属性,正常类无法直接访问,但是可以通过反射访问。如果反射要访问,必须设置属性的可访问权限。

      反射属性对象.setAccessible(true);

  • 方法: (Method):

    1. 得到所有方法:
      getMethods(): 得到所有公开方法
      getDeclaredMethods(): 得到所有方法

    2. 得到单个方法:
      getMethod(): 得到单个公开方法
      getDeclaredMethod():得到单个所有方法

      方法反射对象.invoke(); //解析方法

  • 构造方法: (Constructor)
    1. 得到所有的构造方法
      getConstructors(): 得到所有公开构造方法
      getDeclaredConstructors():得到所有构造方法
    2. 得到单个构造方法
      getConstructor():得到公开的单个构造方法
      getDeclaredConstructor(): 得到单个构造方法
    3. 如何输出构造方法中的内容:
      构造方法对象.newInstance(); //调用了无参构造方法
      构造方法对象.newInstance(参数列表);://调用了带参构造方法

看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//对属性进行操作
public class Test2 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {

//1.得到类对象
Student s=new Student(); //产生了学生对象
Class<?> c=s.getClass();

//2.通过类对象得到单个属性
Field field=c.getDeclaredField("age");

System.out.println("反射得到的属性对象是:"+field);

//得到属性名字
String name=field.getName();

System.out.println("学生类中的age属性名称是:"+name);

//给属性赋值
field.set(s,18);

//得到属性值
Object value=field.get(s);

System.out.println("学生类中age属性的值是:"+value);

//得到name的值
field=c.getDeclaredField("name");

String name2=field.getName();

System.out.println("学生类中的name属性名称是:"+name2);

//反射访问私有属性
field.setAccessible(true);

//给name属性赋值
field.set(s,"zhangsan");

//得到属性值
Object obj2=field.get(s);

System.out.println("学生类中的age属性赋的值是:"+obj2);
}
}



/**
* 反射操作方法
*/
public class Test05 {

public static void main(String[] args) throws ClassNotFoundException {

//1.得到类对象
Class<?> c=Class.forName("com.bjpowernode.test.Student");

//2.通过类对象得到所有的方法
Method[] methods=c.getDeclaredMethods();

//3.遍历
for(Method method:methods) {

System.out.println("学生类中的所有的方法对象:"+method);

//得到方法名
String name=method.getName();

System.out.println("方法名称是:"+name);

//得到方法返回类型
System.out.println("方法返回类型是:"+method.getReturnType());

}
}
}



/*
解析方法
*/
public class Test2 {
public static void main(String[] args) {
//1.得到类对象
Class<?> c=Student.class;

//2.通过类对象得到单个方法对象
Method method=c.getDeclaredMethod("show");

//产生一个学生类的实际对象
Student s= (Student) c.newInstance();
//3.反射实现方法调用
method.invoke(s);
}
}

/*
带参数的解析方法
*/
public class Test07 {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

//1.产生类对象
Class<?> c=Student.class;

//2.通过c调用getDeclaredMethod()得到单个方法
Method method=c.getDeclaredMethod("sum",int.class,int.class);

//2.5产生学生对象
Student s= (Student) c.newInstance();

//3.调用此sum
Object o=method.invoke(s,2,3);

//4.打印最终方法调用返回结果
System.out.println("方法sum调用后,结果为:"+o);
}
}

/*
构造方法
*/
public class Test09 {

public static void main(String[] args){

//1.得到类对象
Class<?> c=Student.class;

//2.得到构造方法对象
Constructor cc=c.getDeclaredConstructor();

System.out.println("反射得到的无参构造方法是:"+cc);

//3.带参构造方法
Constructor ccc=c.getDeclaredConstructor(String.class,int.class);

System.out.println("反射得到的带参构造方法是:"+ccc);

System.out.println("*************************************************");

//输出无参构造方法中的内容
//得到无参构造方法对象
//Student sss=new Student();
cc.newInstance();

//输出带参构造方法中的内容
Object o=ccc.newInstance("zhangsan",18);

System.out.println("学生对象是:"+o);
}
}

学生类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Student {
private String name; //姓名
public int age;
protected String hobby;
int score;

public Student() {

System.out.println("这是学生类中的无参构造方法");
}

public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是学生类中的带参构造方法");
}

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 void show() {

System.out.println("这是学生类中的show方法");

}

public int sum(int x,int y) {

return x+y;
}
}

注解(Annotation)

  • 概念
    注解是一个引用数据类型: ,用来给属性,类,或方法等做约束使用的。

  • jdk自带注解:
    @Override :重写
    @SuppressWarnings : 压下警告
    @Deprecated : 过时

  • 自定义注解:
    步骤:

    1. 先定义一个注解(像类一样
      语法:

      1
      2
      3
      4
      5
      6
      public @interface 注解名称 {

      注解的属性
      (这里的属性按照变量的方式去写)
      访问修饰符 数据类型 属性名();
      }
    2. 定义此注解的元注解

      元注解: 注解的注解

      @Target: 目标:交代你自定义的注解,稍后打在何处(位置):可以打在类处,方法处,属性处,……
      @Rentention:保留 把注解保留在哪个地方:

      1)Source: 源文件中 : Test01.java
      2)Class: 字节码中 : Test01.class
      3)Runtime:运行时: 注解保存在字节码中,但是注解可以通过反射找到。

    3. 使用你的注解

代理

  • 静态代理和动态代理的区别
    静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些。
    但是静态代理代码冗余大,一但需要修改接口,代理类和委托类都需要修改。

  • JDK动态代理和CGLIB动态代理的区别
    JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final。

例子:通过代理输出
张三使用JDK动态代理,李四使用CGLIB动态代理

Speaker接口

1
2
3
public interface Speaker {
void speak();
}

张三实体实现speaker接口

1
2
3
4
5
6
7
public class Zhangsan implements Speaker{

@Override
public void speak() {
System.out.println("张三出声。。。");
}
}

JDK代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 需要实现 InvocationHandler
public class JdkLawyer implements InvocationHandler {

private Object obj;

// 传参构造方法
public JdkLawyer(Object obj) {
this.obj = obj;
}

invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理ing");
method.invoke(obj, args); //反射
return null;
}
}

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Proxy;

public class Demo {
public static void main(String[] args) {
//jdk动态代理
JdkLawyer jdkLawyer = new JdkLawyer(new Zhangsan());

Speaker speaker1 = (Speaker) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Speaker.class}, jdkLawyer);
speaker1.speak();
}
}

// 最后输出:
// jdk动态代理ing
// 张三出声。。。

需要导包

1
2
3
4
5
6
<!--代理包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

李四实体类

1
2
3
4
5
public class Lisi {
public void speak() {
System.out.println("李四出声。。。");
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 实现MethodInterceptor
public class CglibLawyer implements MethodInterceptor {

private Object obj;

// 构造方法
public CglibLawyer(Object obj) {
this.obj = obj;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib动态代理ing");
method.invoke(obj, objects);
return null;
}
}

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import net.sf.cglib.proxy.Enhancer;

public class Demo {
public static void main(String[] args) {

// Cglib动态代理
CglibLawyer cglibLawyer = new CglibLawyer(new Lisi());
Lisi lisi = (Lisi)Enhancer.create(Lisi.class, cglibLawyer);
lisi.speak();
}
}
// 最后输出:
// Cglib动态代理ing
// 李四出声。。。

Lambda表达式

ambda表达式, 从本质来讲, 是一个匿名函数。 可以使用使用这个匿名函数, 实现接口中的方法。
对接口进行非常简洁的实现, 从而简化代码。

实际上, 我们在写lambda表达式的时候, 也不需要关心返回值类型。
我们在写lambda表达式的时候, 只需要关注两部分内容即可: 参数列表方法体

基础

基础语法:

1
2
3
4
5
6
7
8
9
10
11
(参数) -> {
方法体
}

// 参数部分 : 方法的参数列表, 要求和实现的接口中的方法参数部分一致,
// 包括参数的数量和类型。

// 方法体部分 : 方法的实现部分, 如果接口中定义的方法有返回值,
// 则在实现的时候, 注意返回值的返回。

// -> : 分隔参数部分和方法体部分。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 接口
@FunctionalInterface
public interface SingleReturnMutipleParameter {
int test(int a, int b);
}


// 测试
public class BasicSyntax {
public static void main(String[] args) {
// 实现多个参数, 有返回值的函数式接口
SingleReturnMutipleParameter lambda6 = (int a, int b) -> {
System.out.println("这是多个参数, 有返回值的方法");
return a + b;
};
int ret3 = lambda6.test(100, 200);
System.out.println(ret3);

// 最后输出以下结果
// 这是多个参数, 有返回值的方法
// 300
}
}

进阶

  • 参数部分的精简

    • 参数的类型
      由于在接口的方法中,已经定义了每一个参数的类型是什么。 而且在使用lambda表达式实现接口的时候, 必须要保证参数的数量和类型需要和接口中的方法保持一致。 因此, 此时lambda表达式中的参数的类型可以省略不写。

      注意事项:
      如果需要省略参数的类型, 要保证: 要省略, 每一个参数的类型都必须省略不写。 绝对不能出现, 有的参数类型省略了, 有的参数类型没有省略。

    • 参数的小括号
      如果方法的参数列表中的参数数量 有且只有一个 ,此时,参数列表的小括号是可以省略不写的

      注意事项:
      只有当参数的数量是一个的时候, 多了、少了都不能省略。
      省略掉小括号的同时, 必须要省略参数的类型。

  • 方法体部分的精简

    • 方法体大括号的精简
      当一个方法体中的逻辑, 有且只有一句的情况下, 大括号可以省略。
    • return的精简
      如果一个方法中唯一的一条语句是一个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。

函数引用

在有些情况下, 我们需要在lambda表达式中实现的逻辑, 在另外一个地方已经写好了。
此时我们就不需要再单独写一遍, 只需要直接引用这个已经存在的方法即可

注意事项:
在引用的方法后面, 不要添加小括号。
引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。

  • 语法:
    类::静态方法
    对象::非静态方法

函数引用: 引用一个已经存在的方法, 使其替代lambda表达式完成接口的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

public class Lambda1 {

// 接口
private static interface Calculate {
int calculate(int a, int b);
}

private int calculate2(int a, int b) {
if (a != b) {
return a - b;
}
return a + b;
}

private static int calculate(int x, int y) {
if (x > y) {
return x - y;
} else if (x < y) {
return y - x;
}
return x + y;
}

public static void main(String[] args) {
// 单纯的引用 ,换成冒号的写法更优雅
Calculate calculate0 = (x, y) -> calculate(x, y);
//
// 引用一个静态方法
Calculate calculate = Lambda1::calculate;
// 调用
System.out.println(calculate.calculate(10, 10));

// 引用一个非静态方法
Calculate calculate2 = new Lambda1()::calculate2;
// 调用
System.out.println(calculate2.calculate(10, 20));
}
}

构造方法的引用

如果某一个函数式接口中定义的方法, 仅仅是为了得到一个类的对象。 此时我们就可以使用
构造方法的引用, 简化这个方法的实现。

  • 语法
    类名::new
  • 注意事项
    可以通过接口中的方法的参数, 区分引用不同的构造方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class demo {

// 一个类
private static class Person {
String name;
int age;

public Person() {
System.out.println("一个Person对象被实例化了");
}
}

// 接口
@FunctionalInterface
private interface GetPerson {
// 仅仅是希望获取到一个Person对象作为返回值
Person test();
}
public static void main(String[] args) {
// lambda表达式实现接口
GetPerson lambda = Person::new; // 引用到Person类中的无参构造方法,获取到一个Person对象

// 调用
lambda.test();
//输出
//一个Person对象被实例化了
}
}

对象方法的特殊引用

如果在使用lambda表达式,实现某些接口的时候。 lambda表达式中包含了某一个对象, 此时方法体
中, 直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。 其他的参数, 可以作为调用方法
的参数。 此时, 可以对这种实现进行简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static class Person {
private String name;

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

public String getName() {
return name;
}
}

@FunctionalInterface
private interface MyInterface {
// String get(Person person);
void set(Person person, String name);
}

public static void main(String[] args) {
Person xiaoming = new Person();
xiaoming.setName("xiaoming");
//
// MyInterface lambda1 = x -> x.getName();
// MyInterface lambda2 = Person::getName;
// System.out.println(lambda2.get(xiaoming));

MyInterface lambda1 = (x, n) -> x.setName(n);
MyInterface lambda2 = Person::setName;
lambda2.set(xiaoming, "456");
System.out.println(xiaoming.getName());
// 成功修改成456
}

闭包问题

如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。 是一个常
量, 不能修改值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Lambda4 {
private static int y = 20;
public static void main(String[] args) {
// 1. 定义一个局部变量
int x = 10;
// 2. 使用lambda表达式实现接口
LambdaTest lambda = () -> {
System.out.println("x = " + x);
System.out.println("y = " + y);
};
// 3. 修改变量x的值,发现会报错
// x = 20;

// 4. 修改变量y的值,不会报错
y = 200;

lambda.test();
}
}

@FunctionalInterface
interface LambdaTest {
void test();
}

实例

  • 线程的实例化
    1
    2
    3
    4
    5
    public static void main(String[] args) {
    Thread thread = new Thread(() -> {
    // 线程中的处理
    });
    }
  • 集合的常见方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    Collections.addAll(list, "我", "是", "葫芦", "呀?");
    // 按照条件进行删除
    list.removeIf(ele -> ele.endsWith("?"));
    // 批量替换
    list.replaceAll(ele -> ele.concat("!"));
    // 自定义排序
    list.sort((e1, e2) -> e2.compareTo(e1));
    // 遍历
    list.forEach(System.out::println);
    }

    // 最后输出:
    //葫芦!
    //是!
    //我!
  • 集合的流式编程
    1
    2
    3
    4
    5
    6
    7
        public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    Collections.addAll( list, "我", "是", "葫芦", "呀?");
    list.parallelStream().filter(ele -> ele.length() > 1).forEach(System.out::println);
    }
    // 葫芦
    // 呀?