爆肝!!!!JavaSE知识点1.3w字总结

爆肝!!!!JavaSE知识点1.3w字总结

Java基础语法知识

关键名词

jdk

jdk(java development kit),即Java开发工具集。

  • SUN公司为JDK工具包提供了一整套文档资料,我们习惯上称
    之为JDK文档。
  • JDK文档中提供了Java中的各种技术的详细资料,以及JDK中提
    供的各种类的帮助说明 。
  • JDK文档是Java语言的完整说明,大多数书籍中的类的介绍都
    要参照它来完成,它是编程者经常查阅的资料。

jre

jre(java runtime enviroment),即Java运行环境

jvm

jvm(java virtual machine),Java虚拟机。目前主要使用的式hotsopt。

Java历史

sun公司

  • 美国SUN(Stanford University Network)公司
  • 在中国大陆的正式中文名为“太阳计算机系统(中国)有限公司”
  • 在台湾中文名为“升 阳电脑公司”。

Java为什么被发明

  • Green项目

  • 应用环境:像电视盒这样的消费类电子产品

  • 要求:

    • 语言本身是中立的,也就是跨平台
  • 发明者:James Gosling

  • 历程:

▪ 1991年,Sun公司的Green项目,Oak
▪ 1995年,推出Java测试版
▪ 1996年,JDK1.0
▪ 1997年,JDK1.1
▪ 1998年,JDK1.2,大大改进了早期版本的缺陷,是一个革命性的版本,更名为Java2
▪ 1999 Java被分成J2SE、J2EE 和J2ME,JSP/Servlet技术诞生
▪ 2004年,J2SE 5.0 (1.5.0) Tiger老虎.为了表示这个版本的重要性,J2SE1.5更名为J2SE5.0。
▪ 2006年,J2SE 6.0 (1.6.0) Mustang野马.此时,Java的各种版本被更名,取消其中的数字"2":J2EE更名为Java EE, J2SE更名为
Java SE,J2ME更名为Java ME
▪ 2009年4月20日甲骨文收购Sun公司,交易价格达74亿美元
▪ 2011年,JavaSE7.0
▪ 2014年 ,JavaSE8.0
▪ 2017年,JavaSE9.0
▪ 2018年3月,JavaSE10.0
▪ 2018年9月,JavaSE11.0
▪ 2019年3月,JavaSE12.0

第一个Java程序

public class Test{
    public static void main(String[] args){
        System.out.println("hello, world");
    }
}

标识符

定义

用来给类、变量、方法、对象等起的名称。因为在Java中一切皆对象,故不存在函数这一说,只有方法

规则

  • 首字母可以是字母、下划线、美元符号,但是不能是数字

    • // 合法
      int a; 
      int _a;
      int A;
      int $a;
      // 不合法
      int 1a;
      
  • 其他部分只能是字母、下划线、数字、美元符号

    • // 合法
      int a1_$;
      // 不合法
      int a-1;
      
  • Java严格区分大小写

    • int a;
      int A;
      // a和A不是同一个变量
      
  • Java命名变量的时候建议采用驼峰标识以及变量要见名知意

    • // 建议
      String name;
      int age;
      boolean isAlive;
      // 不建议
      String s;
      int a;
      boolean b;
      

保留字

定义

给系统使用的关键字,比如:int,float,void, class,for,while,break等等,在定义变量的时候不能定义成:int float;

运算符

算术运算符

算术运算符包括:+,-,*,/,%。

int a = 10;
int b = 3;
a + b; // 13
a - b; // 7
a * b; // 30
a / b; // 作商, 3
a % b; // 取余, 1

比较运算符

比较运算符包括:==,!=,>,>=,<, <=。

int a = 10;
int b = 3;
a == b; // false
a != b; // true
a > b ; // true
a >= b; // true
a < b ; // false
a <= b; // false

赋值运算符

赋值运算符:int a = 10;,将10赋值给变量a。

逻辑运算符

逻辑运算符包括:&&,||,!。

int a = 10;
int b = 1;
int c = 2;
// && 两边同时为true结果返回true,否则返回false
(a > b) && (a > c); // true,a>b为true,a>c为true
(a > b) && (a < c); // false, 因为,a>b为true,a<c为fasle
// || 两边只要有一边为true,结果返回true
(a > b) || (a > c); // true,a>b为true,a>c为true
(a > b) || (a < c); // true,a>b为true,a<c为false
(a < b) || (a > c); // true,a<b为false,a>c为true
(a < b) || (a < c); // false,a<b为fasle,a<c为false
// !,对取得的布尔值取反操作,!true->false, !false->true
!(a>c); // fasle,a>c为true

// &&,|| 的短路操作
(a > b) && ((c = b) ==b); // true,c=1,因为a>b为true,继续比较右边,执行右边
(a < b) && ((c=b)==b); // false,c=2,因为a<b为false,直接返回false,不比较后面代码,也不执行后面代码
(a > b) || ((c=b)==b); // true,c=2,因为a>b为true,直接返回结果,不执行后面操作
(a < b) || ((c=b)==b); // true,c=1,因为a<b为false,继续比较右边,执行右边代码,右边结果为true

三目运算符

形如:xxx?x:y。若xxx语句的返回值为true,则返回x,否则返回y。

int a = 10>20?1:2; // a=2,因为10>20为false

位运算符

位运算符主要包括:&,|,^。

// & 将两个数转换为二进制进行比较,相同的位上都是1,则该位比较后的值为1
// | 将两个数转换为二进制进行比较,相同的位上如果存在1,则该位比较后的值为1
// ^ 将两个数转换为二进制进行比较,相同的位上如果存在1,且不同时为1,则该位比较后的值为1

5转换成二进制:0101
3转换成二进制:0011
	0101
  & 0011
  = 0001 转换成10进制:1
	0101
  | 0011
  = 0111 转换成10进制:7
	0101
  ^ 0011
  = 0110 转换成10进制:6

数据类型

基本数据类型

byte b;
short s;
int i;
long l;
float f;
double d;
char c;
boolean b;
String string;
整型
  • byte,占1个字节,值得范围是:- 128~127
  • short,占2个字节,值得范围是:-65536~65535
  • int,占4个字节,值得范围是:-4294967296~4294967295
  • long,占8个字节,值得范围是:-18446744073709551616~18446744073709551615
浮点型
  • float,占4个字节,值的范围是:1.4E-45~3.4028235E38
  • double,占8个字节,值的范围是:4.9E-324~1.7976931348623157E308
字符型
  • char,占2个字节
布尔型

占1位。

  • true
  • false

引用数据类型

  • String

  • 数组

    • 定义方式

      • int[] numbers = new int[10];
      • int[] numbers = new int[10]{1,2,3,4,5,6,7,8,9,0};
      • int[] numbers = {1,2,3,4,5,6,7,8,9,0};
    • 使用

      • 获取

        • int[] numbers = {1,2,3};
          System.out.println(numbers[1]);
          
      • 修改

        • int[] numbers = {1,2,3};
          numbers[1] = 10;
          
    • 注意:

      • 数组的下标为:0~数组的长度-1
  • 对象

    • 类的实例化

      • class Person{
            
        }
        Person person = new Person();
        // person即为对象
        
  • 接口

    • 某种行为/功能的抽象,若要使用则要实现接口中的所有方法,接口类似于一种约束、规范

    • interface MyInterface{
          void method();
      }
      
  • 等等

流程控制语句

顺序结构

从上到下依次按顺序执行

分支结构

  • if

    if(条件){
        执行语句
    }
    // 但条件的返回结果为true时执行执行语句,否则不执行
    // 应用
    int a = 10;
    if(a < 20){
        a = 30;
    }
    // 因为 a<20为true,执行语句,a=30
    
  • if...else

    if(条件){
        执行语句
    }else{
        执行语句2
    }   
    // 若条件的返回结果为true,则执行语句,否则执行语句2
    // 应用
    int a = 10;
    if(a>10){
        a = 8;
    }else{
        a = 20;
    }
    // 因为a>10为false,因此执行a=20
    
  • if...else if...else

    • if(条件1){
          执行语句1
      }else if(条件2){
          执行语句2
      }else{
          执行语句3
      }
      // 若满足条件1,则执行语句1,否则若满足条件2,则执行语句2,否则执行语句3
      // 应用
      int a = 10;
      if(a > 10){
          a = 8;
      }else if(a > 5){
          a = 4
      }else{
          a = 100;
      }
      //a = 4,因为a>10为false,a>5为false,执行a=4
      
  • switch

    switch(变量){
        case value1:
            执行语句1
            break;
        case value2:
            执行语句2
            break;
        default:
            执行语句3
            break;
    }
    // 变量的值如果等于某一个case后面的值则执行相应的语句,然后跳出语句,否则就执行default里的语句,然后退出
    // 这里变量以及case的值只能为:整数、字符、字符串
    // 应用
    int a = 10;
    switch(a){
        case 10:
            System.out.println("10");
            break;
        case 20:
            System.out.println("20");
            break;
        default:
            System.out.println("no");
            break;
    }
    // 打印10
    
    

循环结构

  • do...while

    • do{
          执行语句
      }while(条件);
      // 循环执行语句,直到不满足条件,至少做一次
      int a = 0;
      do{
          System.out.print(i);
          i++;
      }while(i<20);
      
  • while

    while(条件){
        执行语句
    }
    // 但满足条件时循环执行语句,直到不满足条件
    int a = 0;
    while(a < 10){
        System.out.println(a);
        a++;
    }
    
  • for

    for(初始化变量;满足的条件;更新变量){
        执行语句
    }
    // 对于初始化变量,每次执行语句后更新变量,直到不满足条件
    for(int a;a<10;a++){
        System.out.println(a);
    }
    
  • 增强for循环

对于一个集合、数组或者map,若其中的元素的类型都一致,则可以使用使用forech(增强for循环来遍历)

for(类型 变量:集合){
    执行语句
}
// 应用
int[] numbers = {1,2,3,4,5};
for(int number:numbers){
    System.out.println(number);
}

流程控制语句的语法规则

  • break

    • 跳出当前循环

      for(int i=0;i<10;i++){
          if(i==5){
              break;
          }
          System.out.println(i);
      }
      //打印0-4后,更新变量,i=5,此时跳出for循环
      
  • continue

    • 跳过本次循环

      • for(int i=0;i<10;i++){
            if(i % 2 !=0){
                continue;
            }
            System.out.println(i);
        }
        // 打印0-9所有的偶数
        
  • return

    • 返回结果值

      • public int addOne(int number){
            return number + 1;
        }
        
        System.out.println(addOne(10));
        // 返回11
        
    • 结束程序

      • for(int i=0;i<10;i++){
            if(i ==5){
                return;
            }
        }
        
        // 当i==5时,结束程序
        

面向对象

编程思想

面向过程编程(POP, Procedure Oriented Programming)

典型的是C语言。通过分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。以把大象放入冰箱为例,我们首先定义一个拿到大象的函数,然后定义一个开冰箱的函数,然后定义一个放大象的函数,然后定义一个关冰箱的函数,最后一次调用就可以实现功能了。

在考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现。

面向对象编程(OOP,Object Oriented Programming)。

典型的C++、C#、Java等编程语言。通过将现实世界中的物体抽象成一个具体的类,通过调用类的相关方法来完成一系列行为。还是以把大象放入冰箱为例,我们先定义一个人的类,有拿大象、放大象、开冰箱、关冰箱的方法(也就是人可以做这些事情),再定义一个大象类,再定义一个冰箱类,然后创建相应的对象调用相关的方法即可。

在考虑问题时,以对象为单位,考虑它的属性及方法。

面向接口编程(IOP,Interface Oriented Programming)

定义(规范,约束)与实现(名实分离的原则)的分离。

接口的本身反映了系统设计人员对系统的抽象理解。

面向切面编程(AOP,Aspect Oriented Programming)

通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

函数时编程(Function Programming)

典型的是Python、JavaScript语言。

函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。主要思想是把运算过程尽量写成一系列嵌套的函数调用。

类和对象

类是对象的抽象,对象是类的实例化

在现实生活中,人(Person)表示一种生物,很抽象,具体的人:张三,就是一个对象,一个具体的人,一个独立的个体。

// 定义一个类
class Person{
    
}
// 实例化一个对象(创建一个对象)
Person zhangsan = new Person();
如何创建对应的对象

通过new关键字实现。

构造器

一个类的构造方法,用于对一个对象的初始化,比如说我们在买电脑的时候都会选择电脑的:cpu、内存、硬盘、显示屏等等,这些相当于初始化。

class Computer{
    private String cpu; 
    private String arm;
    private String disk;
    private String screen;
    
    public Computer(String _cpu, String _arm, String _disk, String _screen){
        cpu = _cpu;
        arm = _arm;
        disk = _disk;
        screen = _screen;
    }
}
// 利用构造器创建对象
Computer myComputer = new Computer("Intel", "英伟达","sanxing", "英伟达");

默认情况下建议写一个无参构造器。

this

指代当前对象。可以在构造方法中使用,用来区分参数和成员变量,在普通方法中使用表示调用当前的属性或者当前类的方法,可以省略。

class Computer{
    private String cpu; 
    private String arm;
    private String disk;
    private String screen;
    
    public Computer(String cpu, String arm, String disk, String screen){
        this.cpu = cpu;
        this.arm = arm;
        this.disk = disk;
        this.screen = screen;
    }
}
// 利用构造器创建对象
Computer myComputer = new Computer("Intel", "英伟达","sanxing", "英伟达");
static

表示静态的,更准确的说是表示类的什么什么,被static修饰的方法、函数、类(内部类)在类加载的时候就创建了,而没有被修饰的则是属于对象,只有当创建对象的时候才创建,因为,static修饰的属性、方法、类的创建是先于普通属性、方法的,因此:静态方法中不能存在普通的属性、方法、对象。

被static修饰的属性、方法,类可以直接调用。

class Person{
    static boolean isPerson = true;
    
    static void sleep(){
        
    }
}
// 调用
System.out.println(Person.isPerson);
System.out.println(Person.sleep());
final

表示最终的,不可更改,有三种形式

  • 修饰在属性上,表示属性的值不可更改,若属性的值是一个引用,则不能再被赋予其他的引用,但是引用中的属性可以更改
  • 修饰在方法上,表示该方法不能被重写(重新实现)
  • 修饰在类上,表示类不可被继承
package

打包:区分同名的文件,分布在不同的目录。

将不同的功能代码整合在一起。

import

导包:方便引用其他包的类

访问修饰符

规定方法、类、属性的访问范围。

分类:

  • private,只能当前类中使用
  • default,只能在当前包、类中使用
  • protected,可以在本类、本包以及继承该类的类中使用
  • public,同一个项目下的任何地方都可以使用
方法的重载

在同一个类中,可以定义多个同名方法,但是,这些方法必须满足以下三个条件之一:

  • 参数类型不一样

  • 参数个数不一样

  • 参数顺序不一样

    class Demo{
        public void method(){
            
        }
        public void method(String str){
            
        }
        public void method(String str,int number){
            
        }
        public void method(int number,String name){
            
        }
    }
    

面向对象三大特性

封装
广义的封装

在代码中可以将某个代码块变成方法,抽象出某些工具类

狭义的封装
  1. 将属性设置成私有,不能被外部直接访问
  2. 提供共有的set和get方法,使外部可以进行调用
  3. 可以实现更加复杂细致的内部逻辑判断
  4. 保护类的内部实现细节,只提供公共的对外访问接口,保护类的安全
public class Test2 {
    private String string;
    private int number;

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}
继承
  • 解释说明

    • 子类可以拥有父类的某些属性和方法,但是私有的除外
    • java中式严格意义上的单继承
    • 使用extends关键字来实现继承
  • 重写

    • 当父类中的某些方法不能满足子类的需要的时候,子类可以重写父类的方法,要求跟父类的方法名称一致
    • 子类方法的访问修饰符>父类的访问修饰符
    • 建议添加@override注解,在编译时提示错误
  • super

    • 表示的是父类的对象
    • 在创建对象的时候,可以在子类中直接通过super关键字调用父类的构造方法
    • 在子类方法中可以使用super.的方法调用父类的方法和属性
  • public class Test2 extends Test4{
        @Override
        public void method() {
            super.method1();
            System.out.println("Test2->method");
        }
        
    }
    
    class Test4{
        public void method(){
            System.out.println("Test4->method");
        }
        
        public void method1(){
            System.out.println("Test4->method1");
        }
    }
    
多态
  • 通俗解释:对于同一个指令,不同的对象给予的不同反应
  • 前提
    1. 必须有继承关系
    2. 子类必须重写父类方法
    3. 父类引用指向子类对象
  • 父类和子类对象的转换:
    • 子类到父类,自动转换
    • 父类到子类,强制转换,且通过inatanceof判断
public class Test2{
    public static void main(String[] args) {
        personSpeak(new Boy()); // 传入子类自动转换成父类
        personSpeak(new Girl());
        personSpeak((Person) new Object()); // 父类到子类强制转换,Object类是所有类的父类
    }
    
    static void personSpeak(Person person){
        person.speak();
    }
    

}

abstract class Person{
    abstract void speak();
}

class Boy extends Person{

    @Override
    void speak() { // 重写父类方法
        System.out.println("男孩说话");
    }
}

class Girl extends Person{

    @Override
    void speak() {
        System.out.println("女孩说话");
    }
}
接口
  • 接口代表一种能力,实现接口,则表示具备了某种功能
  • 接口代表一种规范,在实现接口的时候必须遵循相应的规范,比如说重新实现方法的时候参数的要求、返回值的要求等等
  • 接口可以在一定程度上实现java的多继承,java只支持单继承,但可以实现多个接口,可以变相的实现多继承
  • 接口中的方法都是public abstract这样的访问修饰符,不能包含具体的方法实现
  • 子类如果实现了某个接口,那么接口中的所有方法都要被重写,且实现具体的逻辑,如果没有实现的话,那么子类要么是子接口要么是抽象类
  • 接口中的属性都是常量值
public class Test2{
    public static void main(String[] args) {
        new Singer().singSong(new Boy());
    }
    

}

interface Sing{
    final boolean isSing = true;
    void singSong(Person person);
}


class Singer extends Person implements Sing{

    @Override
    public void singSong(Person person) {
        this.speak();
    }

    @Override
    void speak() {

    }
}

抽象类
  1. 抽象类跟接口类似,可以定义抽象方法,也可以包含具体的实现
  2. 抽象类和抽象方法需要使用abstract关键字来进行修饰
  3. 抽象类的子类必须重写抽象类中的抽象方法,如果不重写的话,那么该子类也会成为一个抽象类
  4. 抽象类更能表示多态,对于子类来说需要实现相同的功能且实现细节一样的则公用方法,对于子类来说需要各自实现细节的就重写方法
// 案例:人都要睡觉;人要说话,但是不同国家的人说的话不一样,中国人说中文,美国人说美语
abstract class Person{
    
    public void sleep(){
        System.out.println("是个人都睡觉");
    }
    
    abstract void speak();
}

class Chinese extends Person{

    @Override
    void speak() {
        System.out.println("中国人说作文");
    }
}

class American extends Person{

    @Override
    void speak() {
        System.out.println("美国人说美语");
    }
}

内部类
  • 静态内部类
  • 方法内部类
  • 匿名内部类
  • 成员内部类
public class Demo1 {

    public static void main(String[] args) {
        // 静态内部类
        StaticInnerClass staticInnerClass = new Demo1.StaticInnerClass();
        // 成员内部类
        InnerClass innerClass = new Demo1().new InnerClass();
        // 方法内部类,生命周期指在方法内
        Object o = method();
        // 匿名内部类
        MyInterface myInterface = new MyInterface() {};
    }

    static class StaticInnerClass{

    }

    class InnerClass{

    }

    public static Object method(){
        class MethodInnerClass{

        }
        
        return new MethodInnerClass();
        
    }
}

interface MyInterface{
    
}


异常

定义

表示程序在运行过程中出现的非正常情况,编写代码的过程中尽可能少的减少异常出现的情况

分类

  • Throwable
    • Error 代码级别无法解决的异常情况
    • Exception
      • 运行时异常:在程序运行过程中,由于输入的参数值的不同,可能会发生也可能不会发生
      • checked异常 :编译时异常,在ide中会显示的检查出来

异常的处理

捕获异常
  • try...catch

    try{
        可能出现异常的语句
    }catch(可能出现的异常){
        捕捉异常后执行的语句
    }
    // 
    try{
        int x = 1 /0;
    }catch(ArithmeticException e){
        e.printStack();
    }
    
  • try...catch...finally

    try{
        可能发生异常的代码
    }catch(发生的异常){
        捕捉异常后执行的语句
    }finally{
        不管是否发生异常都要执行的语句
    }
    
    //
    try{
        int x = 1 /0;
    }catch(ArithmeticException e){
        e.printStack();
    }finally{
        System.out.println("finally")
    }
    
  • 注意

    1. 当发生异常时,在try代码块中异常语句后面的语句不会再执行

    2. try、catch、finally以及try...catch...finally之外的代码是互相独立的,在try中定义的变量在其他的部分无法访问,改变了仅作用于try部分,其他catch、finally情况类似,但是外部定义的变量在try、catch、finally中可以使用

    3. 在进行异常捕获的时候,return语句的不同位置会造成结果的不同

      1. 首先讨论try...catch...finally后面有代码的情况

        //此时注意一个关键的点:checked异常,即return语句后面不能再跟语句
        // 例如
        return;
        System.out.println("after return");// 这条语句ide直接报错,相当于多余的
        // 故return的语句只能有一下几种情况
        //1
                try{
                    System.out.println("1");
                    System.out.println(1 / 0);
                    return;
                }catch (Exception e){
                    System.out.println("2");
        //            return;
                }finally {
                    System.out.println("3");
        //            return;
                }
                System.out.println("4");
            }
        // 此时return语句永远都不会被执行,也就不会报错。
        //2
                try{
                    System.out.println("1");
        //            System.out.println(1 / 0);
                    return;
                }catch (Exception e){
                    System.out.println("2");
        //            return;
                }finally {
                    System.out.println("3");
        //            return;
                }
                System.out.println("4");
        //此时,当程序执行到return语句时,并不会立即执行,而是先挂起,等执行完finally中的语句之后再执行return语句结束程序,故try、catch、finally之外的其他代码不会再执行
        //其他的catch、finally中都不能再存在return语句了
        
      2. try...catch...finally后面没有代码的情况

        //第一种情况
        try{
                    System.out.println("1");
                    System.out.println(1 / 0);
                    return;
                }catch (Exception e){
                    System.out.println("2");
        //            return;
                }finally {
                    System.out.println("3");
        //            return;
                }
        // 此时return语句永远不会执行
        // 第二种情况,此时return语句有没有都没有关系
        try{
                    System.out.println("1");
        //            System.out.println(1 / 0);
                    return;
                }catch (Exception e){
                    System.out.println("2");
        //            return;
                }finally {
                    System.out.println("3");
        //            return;
                }
        //第三种情况
        try{
                    System.out.println("1");
                    System.out.println(1 / 0);
        //            return;
                }catch (Exception e){
                    System.out.println("2");
                    return;
                }finally {
                    System.out.println("3");
        //            return;
                }
        //此时,还是跟前面一样的,return先挂起,执行完finally中的代码后再结束程序
        //第三种情况
        public class Demo {
            public static void main(String[] args) {
                System.out.println(method());
        
            }
        
            public static int method(){
                try{
                    System.out.println("1");
                    System.out.println(1 / 0);
        //            return;
                }catch (Exception e){
                    System.out.println("2");
                    return 1;
                }finally {
                    System.out.println("3");
        //            return 2;
                }
        //        System.out.println("4");
                return 0;
            }
        }
        // 此时会执行最近的return语句
        
      3. 综上:

        1. return语句会被挂起,等执行完finally中的语句后再执行
        2. 当存在两个return语句时且都能被执行时,优先执行finally中的return
抛出异常
throws

将异常情况抛出给调用者

注意,如果整个程序的运行过程中都没有异常的处理的话,最终异常会抛给jvm,不太友好,一般都要对异常进行处理

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo1 {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("test.txt");
    }
}

自定义异常

java中提供了非常多的异常类,但是在某些项目中需要自己定义独特的异常处理方式,此时需要自定义异常

  1. 继承Exception
  2. 编写构造方法,可以传入自己想打印的异常信息
  3. 调用的时候通过throw向外抛出异常
public class MyException extends Exception{
    public MyException(){
        System.out.println("自定义异常");
    }
}
public class Demo1 {
    public static void main(String[] args) throws MyException {
        throw new MyException();
    }
}

另外,还可以自定义异常信息

public class Demo1 {
    public static void main(String[] args) throws Exception {
        throw new Exception("test");
    }
}

常用类

包装类

  1. 基本数据类型都由对应的包装类

    • int:Integer
    • byte:Byte
    • short:Short
    • long:Long
    • float:Float
    • double:Double
  2. 自动拆箱和自动装箱

  3. 举例

    public class Demo {
        public static void main(String[] args) {
            // 此时自动调用:Integer.valueOf(),若数字大于-128且小于127,直接返回值,否则新建一个对象
            Integer integer1 = 1000;
            Integer integer2 = 1000;
            int i1 = 1000;
            int i2 = 1000;
            System.out.println(integer1 == integer2); // 因为两个变量不是同一个对象因此结果为false
            System.out.println(integer1.equals(integer2));// true
            System.out.println(i1 == i2); // true
            System.out.println(i1 == integer2); // true
        }
    }
    

String

  • 不可变字符串:String

  • 可变字符串:

    • 线程安全:StringBuffer
    • 线程不安全:StringBuilder
  • 举例

    public class Demo1 {
        public static void main(String[] args) {
            StringBuffer stringBuffer = new StringBuffer("hello,world");
            // 增
            stringBuffer.insert(0, "@");
            stringBuffer.append("#");
            // 删
            stringBuffer.delete(0,1);
            // 改
            stringBuffer.setCharAt(0, '$');
            stringBuffer.replace(0,1,"x");
            // 查
            stringBuffer.indexOf("a");
            stringBuffer.substring(0,1);
            System.out.println(stringBuffer);
    
    
        }
    }
    

Date

日期常用类

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

public class Demo2 {
    public static void main(String[] args) {
        DateFormat dateFormat = new SimpleDateFormat("YY-MM-dd HH-mm-ss"); // 创建格式化日期类
        System.out.println(dateFormat.format(new Date()));
    }
}

Calender

返回格林威治时间

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

public class Demo3 {
    public static void main(String[] args) {
        Calendar calendar = new GregorianCalendar();
        System.out.println(calendar.getTime());
        System.out.println(calendar.getTimeZone());
        System.out.println(calendar.getFirstDayOfWeek());
        System.out.println(calendar.getWeekYear());
    }
}

Math

数学常用类

public class Demo4 {
    public static void main(String[] args) {
        System.out.println(Math.max(10,20));
        System.out.println(Math.abs(-1));
        System.out.println(Math.ceil(1.1));
        System.out.println(Math.PI);
        System.out.println(Math.E);
    }
}

Random

import java.util.Random;

public class Demo5 {
    public static void main(String[] args) {
        System.out.println(new Random().nextInt(10));
        System.out.println(new Random().nextDouble());
    }
}

Enum(枚举类)

枚举类也是一个类,可以定义属性和方法,甚至可以写构造方法,填写具体的枚举类对象的时候,可以带参数,但是要跟构造方法进行匹配

初级用法
public class Demo6 {
    public static void main(String[] args) {
        System.out.println(Gender.FEMALE);
    }
}
public enum Gender {
    MALE,FEMALE;
}

高阶用法

每个枚举的子枚举都可以看作是枚举的子类,可以定义相关方法以及相应的set/get方法来获取子枚举携带的一些信息

public enum WEEK_DAY {
    MONDAY(0),
    TUESDAY(1),
    WEDNESDAY(2),
    THURSDAY(3),
    FRIDAY(4),
    SATURDAY(5),
    SUNDAY(6);

    private int value;
    public void method(int i){
        System.out.println(i);
    }

    public int getValue(){
        return this.value;
    }



    WEEK_DAY(int i) {
        this.value = i;
    }
}

public class Demo6 {
    public static void main(String[] args) {
        System.out.println(WEEK_DAY.FRIDAY.getValue());
    }
}

集合

定义

一个用来储存数据的容器

数组

形如:int[],String[]的数据结构

特点:

  1. 数据的类型必须是一致的
  2. 数组必须在声明的时候显示的给出长度
  3. 数组在插入和删除的时候,频繁的移动元素,效率比较低
  4. 数组在查找元素的时候可以按照下标查找

Collection

具有的基本操作:

  1. 添加,存储的是对象的引用
    1. add
  2. 容器中元素的个数
    1. size
  3. 删除
    1. remove
    2. clear
    3. removeAll
    4. retainAll
  4. 判断元素是否存在集合中
    1. contains
    2. isEmpty
  5. 遍历元素
    1. iterator
List
特点

不唯一,有序

ArrayList

ArrayList实现了长度可变的数组,在内存中分配连续的空间

  • 优点:遍历元素和随机访问元素的效率比较高
  • 缺点:添加和删除需要大量移动元素效率低,按照内容查询效
    率低
LinkedList

LinkedList采用链表存储方式

  • 优点:插入、删除元素时效率比较高
  • 缺点:遍历和随机访问元素效率低下

LinkedList特有方法

    1. addFirst(Object obj) 添加头
    2. addLast(Object obj) 添加尾
    3. offerFirst(Object obj) 1.6版本之后的加头,尾巴
    4. offerLast(Object obj) 1.6版本之后的加头,尾巴
    1. removeFirst() 删除头 获取元素并删除元素
    2. removeLast() 删除尾
    3. pollFirst() 1.6版本之后的删头,删尾
    4. pollLast() 1.6版本之后的删头,删尾
    1. getFirst() 获取头 获取元素但不删除
    2. getLast() 获取尾
    3. peekFirst() 1.6版本之后的获取头,获取尾
    4. peekLast() 1.6版本之后的获取头,获取尾
List接口特有方法
    1. add(index,element) 在指定索引的位置上插入元素
    2. addAll(index,Collection) 在指定的引的位置上插入整个集合的元素
    3. addAll(Collection) 在结束插入整个集合的元素
    1. remove(index)
    1. set(index,element)
    2. 使用element替换指定索引位置上的元素
  1. 查(获取元素)
    1. get(index)
    2. subList(from,to)
    3. listIterator();
Set
特点:唯一,无序
HashSet
  • 采用Hashtable哈希表存储结构

  • 优点:添加速度快,查询速度快,删除速度快

  • 缺点:无序

  • LinkedHashSet

    • 采用哈希表存储结构,同时使用链表维护次序
    • 有序(添加顺序)
Hash表原理

  • 代码验证HashSet的无序性不唯一性
  • 使用HashSet存储自定义对象,重写hashCode方法与equals方法

  • 关键代码

    HashSet hs=new HashSet();//创建HashSet对象
    hs.add(new Person("张三",20));
    hs.add(new Person("李四",22));
    hs.add(new Person("王五",23));
    hs.add(new Person("李四",22));
    
    • HashSet存储进了相同的对象,不符合实际情况
    • 解决方案: 重写equals方法与hashCode方法
    • HashSet的操作
    @Override
    public int hashCode() {
    System.out.println(this.name+".....hashCode");
    return 60;
    }
    
    • hashCode都相同,不符合实际情况,继续升级

      • 修改hashCode方法

        @Override
        public int hashCode() {
        System.out.println(this.name+".....hashCode");
        return this.name.hashCode()+age;
        }
        
  • 总结:

    • HashSet是通过元素的两个方法,hashCode和equals方法来保证元素的唯一性的 。如果元素的HashCode值相同,才会判断equals是否为true ,如果元素的hashCode值不同,不会调用equals方法
TreeSet
  • 采用二叉树(红黑树)的存储结构
  • 优点:有序(排序后的升序)查询速度比List快
  • 缺点:查询速度没有HashSet快
Comparable 接口

所有可以“排序”的类都实现了java.lang.Comparable 接口 compareTo(Object obj) 方法。

该方法:

返回 0 表示 this == obj
返回正数 表示 this > obj
返回负数 表示 this < obj

实现了Comparable 接口的类通过实现 comparaTo 方法从而确定该类对象的排序方式。

public class StrLenComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
if (o1.length()>o2.length()) {
return 1;
}
if (o1.length()<o2.length()) {
return -1;
}
return o1.compareTo(o2);//长度相同, 按字母
}
}
public static void sortDemo(){
List<String> list=new ArrayList<String>();
..添加元素
sop(list);
Collections.sort(list);//按字母排序
sop(list);
//按照字符串长度排序
Collections.sort(list,new StrLenComparator());
sop(list);
}
Iterator接口

所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象。

Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。

Iterator接口定义了如下方法:

boolean hasNext(); //判断是否有元素没有被遍历
Object next(); //返回游标当前位置的元素并将游标移动到下一个位置
void remove(); //删除游标左面的元素,在执行完next之后该
//操作只能执行一次

所有的集合类均未提供相应的遍历方法,而是把遍历交给迭代器完成。迭代器为集合而生,与门实现集合遍历

Iterator是迭代器设计模式的具体实现

Iterator方法:

  • boolean hasNext():判断是否存在另一个可访问的元素
  • Object next():返回要访问的下一个元素
  • void remove():删除上次访问返回的对象

可以使用Iterator遍历的本质 :实现Iterable接口

ListIterator

在迭代过程中,准备添加或者删除元素

ArrayList al=new ArrayList();
al.add("java1");//添加元素
al.add("java2");
al.add("java3");
//遍历
Iterator it=al.iterator();
while(it.hasNext()){
Object obj=it.next();
if (obj.equals("java2")) {
al.add("java9");
}
sop("obj="+obj);
}
ListIterator的作用:解决并发操作异常

在迭代时,不可能通过集合对象的方法(al.add(?))操作集合中的元素,会发生并发修改异常。 所以,在迭代时只能通过迭代器的方法操作元素,但是Iterator的方法是有限的,只能进行判断(hasNext),取出(next),删除(remove)的操作,如果想要在迭代的过程中进行向集合中添加,修改元素等就需要使用 ListIterator接口中的方法

ListIterator li=al.listIterator();
while(li.hasNext()){
Object obj=li.next();
if ("java2".equals(obj)) {
li.add("java9994");
li.set("java002");
}
}

Map

接口存储一组键值对象,提供key到value的映射

Map相关方法
  1. 添加
    1. put(key,value) 添加元素
  2. 删除
    1. clear() 清除所有
    2. remove(key) 根据key去移除
  3. 判断
    1. containsKey(key)是否包含指定的key
    2. containsValue(value) 是否包含指定的值
    3. isEmpty() 判断集合中元素是否为 空
  4. 遍历
    1. get(key)
    2. size()
    3. values()
    4. entrySet()
    5. keySet()

Collections工具类

Collections和Collection不同,前者是集合的操作类,后者是集合接口

Collections提供的静态方法 :

  1. addAll():批量添加
  2. sort():排序
  3. binarySearch():二分查找
  4. fill():替换
  5. shuffle():随机排序
  6. reverse():逆序

集合总结


集合和数组的比较

数组不是面向对象的,存在明显的缺陷,集合弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率,而且不同的集合框架类可适用不同场合。具体如下:

  1. 数组能存放基本数据类型和对象,而集合类中只能存放对象
  2. 数组容易固定无法动态改变,集合类容量动态改变
  3. 数组无法判断其中实际存有多少元素, length只告诉了数组的容量,而集合的size()可以确切知道元素的个数
  4. 集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式
  5. 集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率

Collection和Collections的区别

  1. Collection是Java提供的集合接口,存储一组不唯 一,无序的对象。它有两个子接口List和Set。
  2. Java还有一个Collections类,专门用来操作集合类,它提供了一系列的静态方法实现对各种集合的搜索、排序、线程安全化等操作。

ArrayList和LinkedList的联系和区别

  1. ArrayList实现了长度可变的数组,在内存中分配连续空间。遍历元素和随机访问元素效率比较高
  2. LinkedList采用链表存储方式。插入、删除元素效率比较高

Vector和ArrayList的联系和区别

  1. 实现原理相同,功能相同,都是长度可变的数组结构,很多时候可以互用
  2. 两者的主要区别如下
    1. Vector是早期的JDK接口, ArrayList是替代Vector的新接口
    2. Vector线程安全, ArrayList重速度轻安全,线程非安全
    3. 长度需要增长时, Vector默认增长一倍, ArrayList增长50%(1.5+1)

HashMap和Hashtable的联系和区别

  1. 实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用

  2. 两者的主要区别如下 :

    1. Hashtable是早期的JDK提供的接口, HashMap是新版的JDK提供的接口
    2. Hashtable继承Dictionary类, HashMap实现Map接口
    3. Hashtable是线程安全, HashMap线程非安全
    4. Hashtable不允许null值, HashMap允许null值

IO

流的基本概念

流是指一连串流动的字符,是以先进先出方式发送信息的通道 。

  1. XXX->程序-->输入流

  2. 程序->XXX-->输出流

Java流的分类

输入输出流是相对于计算机内存来说的,而不是相对于源和目标

字节流是 8 位通用字节流,字符流是 16 位 Unicode 字符流

  1. 功能不同

    1. 节点流:可以直接从数据源或目的地读写数据

    2. 处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能

    3. 节点流和处理流的关系

      1. 节点流处于io操作的第一线,所有操作必须通过他们进行

      2. 处理流可以对其他流进行处理(提高效率或操作灵活性)

文件的读写

  1. 用FileInputStream和FileOutputStream读写文本文件

    1. 使用FileInputStream 读文本文件

      1. 引入相关的类

      2. 构造文件输入流FileInputStream对象

      3. 读取文本文件的数据

      4. 关闭文件流对象

        import java.io.FileInputStream;
        import java.io.FileNotFoundException;
        import java.io.IOException;
        
        public class Demo {
            public static void main(String[] args) {
                FileInputStream fileInputStream;
                byte[] bytes = new byte[1024];
                try {
                    fileInputStream= new FileInputStream("test.txt");
                    fileInputStream.read(bytes);
                    System.out.println(new String(bytes));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                fileInputStream.close();
            }
        }
        
        
    2. 使用FileOutputStream 写文本文件

      1. 引入相关的类

      2. 构造文件数据 流输出对象

      3. 将数据写入文 本文件

      4. 关闭文件流对象

        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.nio.charset.StandardCharsets;
        
        public class Demo2 {
            public static void main(String[] args) throws IOException {
                FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
                String string = "aaaaa";
                fileOutputStream.write(string.getBytes(StandardCharsets.UTF_8));
                fileOutputStream.close();
            }
        }
        
  2. 用BufferedReader和BufferedWriter读写文本文件

    1. 使用 BufferedReader 读文本文件

      import java.io.*;
      
      public class Demo3 {
          public static void main(String[] args) throws IOException {
              FileReader fileReader = new FileReader("test.txt");
              BufferedReader bufferedReader = new BufferedReader(fileReader);
              String result = "";
              while ((result = bufferedReader.readLine())!=null){
                  System.out.println(result);
              }
              bufferedReader.close();
              fileReader.close();
          }
      }
      
    2. 使用 BufferedWriter 写文件

      import java.io.BufferedWriter;
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo4 {
          public static void main(String[] args) throws IOException {
              FileWriter fileWriter = new FileWriter("test.txt");
              BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
              String string = "aaaaaaaa\naaaaaa\taaaa\nsdsdsds";
              bufferedWriter.write(string);
              bufferedWriter.close();
              fileWriter.close();
          }
      }
      
      

二进制文件的读写

使用DataInputStream和DataOutputStream读写二进制文件以及基本数据类型数据的读写

  1. 写入数据

    DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
    dos.writeInt(234);
    dos.writeBoolean(false);
    dos.writeDouble(9943.00);
    dos.writeUTF("中国");
    dos.close();
    
  2. 读取数据

    DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
    int num=dis.readInt();
    boolean isFind=dis.readBoolean();
    double price=dis.readDouble();
    String str=dis.readUTF();
    System.out.println(num+"\t"+isFind+"\t"+price+"\t"+str);
    

对象的读写

使用ObjectInputStream和ObjectOutputStream读写对象(序列化与反序列化)

注意:传输的对象需要实现Serializable接口

  1. 序列化

    ObjectOutputStream oos=new ObjectOutputStream(new
    FileOutputStream("obj.txt"));
    oos.writeObject(new Person("张三",19));
    oos.close();
    
  2. 反序列化

    ObjectInputStream ois=new ObjectInputStream(new
    FileInputStream("obj.txt"));
    Person p=(Person)ois.readObject();
    System.out.println(p);
    
  3. 注意:

    1. 类必须实现Serializable接口
    2. 给类加个序列化编号,给类定义一个标记,新的修改后的类还可以操作曾经序列化的对象
    3. 静态是不能被序列化的,序列化只能对堆中的进行序列化 ,不能对“方法区”中的进行序列化
    4. 不需要序列化的字段前加 transient

总结

  1. 文件操作:FileReader, FileWriter
  2. 字符操作:FileInputStream, FileOutputStream
  3. 基本数据类型操作:DataInputStream, DataOutputStream
  4. 操作对象:ObjectInputStream ,ObjectOutputStream

线程

核心概念

  1. 程序:Program,是一个指令的集合
  2. 进程:Process,(正在执行中的程序)是一个静态的概念
    1. 进程:Process,(正在执行中的程序)是一个静态的概念
    2. 每个进程都是独立的,由3部分组成cpu,data,code
    3. 缺点:内存的浪费, cpu的负担
  3. 线程:是进程中一个“单一的连续控制流程”
    1. 线程又被称为轻量级进程(lightweight process)
    2. 一个进程可拥有多个并行的(concurrent)线程
    3. 一个进程中的线程共享相同的内存单元/内存地址空间->可以访问相同的变量和对象,而且它们从同一堆中分配对象->通信、数据交换、同步操作
    4. 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快

多线程的实现

  1. 在Java中负责线程的这个功能的是Java.lang.Thread 这个类

  2. 可以通过创建 Thread 的实例来创建新的线程

  3. 每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体

  4. 通过调用Thead类的start()方法来启动一个线程

  5. 具体实现:

    1. 继承Thread类
    2. 重写run方法
    3. 创建对象,调用start()方法启动线程
    public class ThreadDemo01 extends Thread {
    //重写父为的run方法
    @Override
    public void run() {
    for(int i=0;i<10;i++){
    System.out.println("第"+i+"次threadrun........");
    }
    }
    public static void main(String[] args) {
    //创建对象,就创建好一个线程
    ThreadDemo01 d=new ThreadDemo01();
    //d.run();//启动线程使用start方法
    d.start();
    for(int i=0;i<5;i++){
    System.out.println("main-->"+i);
    }
    }
    }
    
  6. 线程的执行

    1. xxx.run();仅仅是对象调方法,而是创建了线程但并没有运行
    2. xxx.start();//开启线程并执行该线程的run方法
  1. 操作步骤

    1. 实现Runnable接口
    2. 重写run方法
    3. 创建对象,调用start()方法启动线程
    public class RunableDemo implements Runnable {
    @Override
    public void run() {
    for(int i=0;i<10;i++){
    System.out.println("第"+i+"次threadrun........");
    }
    }
    public static void main(String[] args) {
    //创建对象,就创建好一个线程
    RunableDemo rd=new RunableDemo();
    Thread t=new Thread(rd);
    t.start();
    for(int i=0;i<5;i++){
    System.out.println("main-->"+i);
    }
    } }
    
  1. 继承Thread类方式的缺点:那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread类
  2. 通过Runnable接口实现多线程
    1. 优点:可以同时实现继承。实现Runnable接口方式要通用一些
      1. 避免单继承
      2. 方便共享资源 同一份资源 多个代理访问

线程的状态

  1. 新生状态 :用new关键字建立一个线程后,该线程对象就处亍新生状态 ;处亍新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态
  2. 就绪状态 :处亍就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU;当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称为“CPU调度”
  3. 运行状态 :在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任何而死亡 ;如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态
  4. 阻塞状态 :处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己运行,进入阻塞状态;在阻塞状态的线程并能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续执行
  5. 死亡状态 :死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个,一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过stop方法来终止一个线程【不推荐使用】 ;三是线程抛出未捕获的异常

线程操作的相关方法

阻塞状态(sleep/yield/join方法)

有三种方法可以暂停Thread执行:

  1. sleep:
    不会释放锁, Sleep时别的线程也不可以访问锁定对象。
  2. yield:
    让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
  3. join:
    当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行

多线程的安全性问题

public class TicketImplements implements Runnable {
private int tick=5;
public void run() {
while(true){
if (tick>0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"卖票:"+tick--);
}
}
}
public static void main(String[] args) {
TicketImplements ti=new TicketImplements();
new Thread(ti).start();
new Thread(ti).start();
new Thread(ti).start();
new Thread(ti).start();
}

线程的同步不死锁

多线程的运行出现了安全问题

使用同步解决多线程的安全性问题

  1. 同步代码块

    public void run() {
    while(true){
    synchronized (this) {//通常将当前对象作为同步对象
    if (tick>0) {
    Thread.sleep(10);
    System.out.println(Thread.currentThread().getName()+"卖
    票:"+tick--);
    }
    }
    }
    }
    
    1. 同步的前提

      1. 必须有两个或两个以上的线程
      2. 必须是多个线程使用同一资源
      3. 必须保证同步中只能有一个线程在运行
    2. 将需要同步的代码放到方法中

      public void run() {
      while(true){
      sale();
      }
      }
      public synchronized void sale(){
      //通常将当前对象作为同步对象
      if (tick>0) {
      Thread.sleep(10);
      System.out.println(Thread.currentThread().getName()+"卖票:"+tick--);
      }
      }
      
    3. 同步监视器

      1. synchronized(obj){}中的obj称为同步监视器
      2. 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器
      3. 同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象本身
    4. 同步监视器的执行过程

      1. 第一个线程访问,锁定同步监视器,执行其中代码
      2. 第二个线程访问,发现同步监视器被锁定,无法访问
      3. 第一个线程访问完毕,解锁同步监视器
      4. 第二个线程访问,发现同步监视器未锁,锁定并访问

死锁

  1. 同步可以保证资源共享操作的正确性,但是过多同步也会产生死
  2. 死锁一般情况下表示互相等待,是程序运行时出现的一种问题

线程通信

Java提供了3个方法解决线程之间的通信问题

网络编程

网络的概念

一组相互连接的计算机

网络编程的三要素

  1. IP地址:唯一标识网络上的每一台计算机,两台计算机之间通信的必备要素
  2. 端口号:计算机中应用的标号(代表一个应用程序)0-1024系统使用或保留端口 ,有效端口0-65536
  3. 通信协议:通信的规则TCP,UDP

网络模型

OSI参考模式:开放系统互连参考模型(Open System Interconnect)

TCP/IP参考模型:传输控制/网际协议 Transfer Controln Protocol/Internet Protocol

IP地址的表示方法

  1. IP 地址: 32位, 由4个8位二进制数组成
  2. IP表示方法:点分十进制
  3. 举例
    1. 二进制:11000000.10101000.00000001.11001000
    2. 十进制:192.168.1.200
  4. IP地址 = 网络ID +主机ID
    1. 网络ID:标识计算机或网络设备所在的网段
    2. 主机ID:标识特定主机或网络设备

**IP地址所对应的对象->InetAddress **

获得百度主机名:

InetAddress ia2=InetAddress.getByName("www.baidu.com");
System.out.println("其它主机名称:"+ia2.getHostAddress());

端口

  1. 端口:port
    端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过
    端口,可以在一个主机上运行多个网络应用程序。

传输协议

  1. UDP:相当于収短信(有字数限制),
    不需要建立连接,
    数据报的大小限制在64k内,
    效率较高,不安全,容易丢包
  2. TCP:相当于打电话,需要建立连接,
    效率相对比较低,数据传输安全,
    三次握手完成。
    (点名->答到->确认)

Socket套接字

  1. 网络上的两个程序通过一个双向的通信连接实现数据的交换, 这个连接的一端称为一个socket。
  2. Java中使用Socket完成TCP程序的开収,使用此类可以方便的建立可靠的、 双向的、 持续性的、 点对点的通讯连接
  3. 在Socket的程序开发中,服务器端使用ServerSocket等待客户端的连接,对于java的网络程序来讲,每一个客户端都使用一个Socket对象表示

基于TCP协议的Socket编程

进行网络通信时, Socket需要借助数据流来完成数据的传递工作

  1. 客户端

    1. 建立连接
    Socket socket=new Socket("localhost",8800);
    
    1. 打开Socket关联的输入输出流
    OutputStream os=socket.getOutputStream();
    
    1. 数据流中读写信息
    String info="用户名: Tom;用户密码: 123456";
    os.write(info.getBytes());
    socket.shutdownOutput();
    
    1. 关闭所有的数据流和Socket

      os.close();
      socket.close();
      
  2. 服务端

    1. 建立连接,处理发送到指定端口的数据

      ServerSocket server=new ServerSocket(8800);
      
    2. 获取客户端对象

      Socket socket=server.accept();
      
    3. 数据流中读写信息

      InputStream is=socket.getInputStream();
      byte[] buf=new byte[1024];
      int len=is.read(buf);
      syso(new String(buf,0,len))
      socket.shutdownInput();
      
    4. 关闭所有的数据流和Socket

      is.close();
      socket.close();
      server.close()
      

Socket中实现对象的传递

序列化对象

User user=new User();//User是用户类
user.setLoginName("Tom");
user.setPwd("123456");
oos.writeObject(user);

基于UDP的网络编程

通信双发不需要建立连接 ,通信双方完全平等

import java.io.IOException;
import java.net.*;


public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        DatagramSocket datagramSocket = new DatagramSocket(new InetSocketAddress(InetAddress.getByName("localhost"),8888));
        datagramSocket.connect(InetAddress.getByName("localhost"), 9999);
        ReadThread readThread = new ReadThread(datagramSocket);
        WriteThread writeThread = new WriteThread(datagramSocket);
        readThread.start();
        writeThread.start();
    }
}

public class Client2 {
    public static void main(String[] args) throws IOException, InterruptedException {
        DatagramSocket datagramSocket = new DatagramSocket(new InetSocketAddress(InetAddress.getByName("localhost"),9999));
        datagramSocket.connect(InetAddress.getByName("localhost"), 8888);
        ReadThread readThread = new ReadThread(datagramSocket);
        WriteThread writeThread = new WriteThread(datagramSocket);
        readThread.start();
        writeThread.start();
    }
}

import java.nio.charset.StandardCharsets;

public class ReadThread extends Thread{
    private DatagramSocket datagramSocket;
    private byte[] bytes = new byte[1024];
    private byte[] bytes1;
    DatagramPacket datagramPacket = new DatagramPacket(this.bytes, 0,1024);

    public ReadThread(DatagramSocket datagramSocket){
        this.datagramSocket = datagramSocket;
    }
    @Override
    public void run() {
        super.run();
        while (true){
            try {
                this.datagramSocket.receive(this.datagramPacket);
                this.bytes1 = new String(this.bytes, 0, this.bytes.length).getBytes(StandardCharsets.UTF_8);
                System.out.println(new String(this.bytes1, 0, this.bytes1.length));
                Thread.sleep(2);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

import java.util.Scanner;

public class WriteThread extends Thread{
    private DatagramSocket datagramSocket;
    private String message = "";
    private byte[] bytes = new byte[1024];
    private Scanner scanner = new Scanner(System.in);

    public WriteThread(DatagramSocket datagramSocket){
        this.datagramSocket = datagramSocket;
    }

    @Override
    public void run() {
        super.run();
        while (true){
            synchronized (WriteThread.class){
                System.out.print("输入:");
                this.message = this.scanner.nextLine();
            }
            DatagramPacket datagramPacket = null;
            try {
                datagramPacket = new DatagramPacket(message.getBytes(StandardCharsets.UTF_8),0, message.length(), new InetSocketAddress(InetAddress.getByName("localhost"),datagramSocket.getPort()));
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            try {
                datagramSocket.send(datagramPacket);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Lambda表达式

Lambda表达式的介绍

Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看起来更简洁易懂。 Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作

Lambda表达式的特点

  1. 函数式编程
  2. 参数类型自动推断
  3. 代码量少,简洁

Lambda表达式案例

Lambda表达式应用场景

任何有函数式接口的地方

函数接口

只有一个抽象方法(Object类中的方法除外) 的接口是函数式接口

  1. Supplier 代表一个输出
  2. Consumer 代表一个输入
  3. BiConsumer 代表两个输入
  4. Function 代表一个输入,一个输出(一般输入和输出是不同类型的)
  5. UnaryOperator 代表一个输入,一个输出(输入和输出是相同类型的)
  6. BiFunction 代表两个输入,一个输出(一般输入和输出是不同类型的)
  7. BinaryOperator 代表两个输入,一个输出(输入和输出是相同类型的)

方法的引用

方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用

方法引用的分类

  1. 静态方法引用:如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用
  2. 实例方法引用:如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用
  3. 对象方法引用:抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用
  4. 构造方法引用:如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用
posted @ 2021-02-20 15:09  JonnyJiang-zh  阅读(156)  评论(0编辑  收藏  举报