Java基础自查手册

本文最后更新于:2021年6月19日 晚上

一、注释

1. 单行注释
格式: // 注释文字
    
2. 多行注释
格式: /* 注释文字 */
    
3. 文档注释
格式: /** 注释文字 */

二、关键字

被Java语言赋予特定含义的单词,组成关键字的字母全部小写

三、常量

1.概念:

在程序的运行过程中, 其值不会发生改变的量.

2.字面值常量的分类:

  • 字符串常量: “abc” 值必须用””括起来
  • 字符常量: ‘a’, ‘0’ 值必须用’’括起来
  • 整数常量: 1,2,3,4
  • 小数常量: 1.2
  • 布尔常量: true, false, 值只有两个.
  • 空常量: null

3.自定义常量:

采用final关键字修饰的量

四、变量

1.概念

在程序执行的过程中,其值可以在某个范围内发生改变的量。 变量的本质,是内存中的一小块区域

2.定义格式

数据类型 变量名 = 初始化值;

byte b = 10;
int i = 100;

3.数据类型

变量变化的范围就是数据类型。

设计多种数据类型可以帮助我们更充分的利用内存空间,提高内存使用的效率。

注意事项:

  • 整形默认是int类型, 定义long类型变量的时候, 后边要加字母L(大小写均可)
  • 浮点型默认是double类型, 定义float类型变量的时候, 后边要加字母F(大小写均可)
  • 变量未赋初值, 不能使用;

  • 变量只在它所属的那对大括号内(变量的作用域)有效.

// long类型的数据
long l = 10000000000L;

// float类型的数据
float f = 12.3F;

4.数据类型转换

不同类型的数据由于取值范围不同,存储方式不同,直接进行运算可能会造成数据损失,所以需要将一种类型转换成另外一种类型再进行运算。

public class ConversionDemo1 {
    public static void main(String[] args) {
        //定义一个int类型的变量 和 一个byte类型的变量
        int aa = 2;
        byte bb = 3;
        System.out.println(aa + bb);    //5
       //byte cc = aa + bb;      //代码报错 涉及到了 自动类型转换.
        int dd = aa + bb;        
        // 目标类型 变量名 = (目标类型)要转换的值;
        byte cc = (byte)(aa + bb);      //强制类型转换
        System.out.println(cc);
        System.out.println(dd)

        double d = 3.2;
        int ee = 2;
        double d2 = d + ee;
        int i = (int)(d + ee);
        System.out.println(d2);     //5.2
        System.out.println(i);      //5     因为丢失了精度.
    }
}

五、标志符

1.概念

标识符就是在编程中程序员给类、方法、变量、常量等起的名字

2.组成:

英文大小写字母, 数字, _, $

3.命名规则:

  • 类和接口: 所有单词的首字母都大写, 其他小写. HelloWorld, Student
  • 方法和变量: 从第二个单词开始, 所有单词的首字母大写, 其他全部小写. getName, studyJava
  • 常量: 全部大写, 单词之间用_隔开. MAX_VALUE
  • 包名: 全部小写, 多级包之间用.隔开, 一般是公司的域名反写. cn.itcast.demo
  • 总结:遵循驼峰命名, 见名知意.

4.注意事项:

  • 不能以数字开头.

  • 不能和Java中的关键字重名.

  • 严格区分大小写.

六、运算符

1.概念

对变量和常量进行运算操作的符号

2.分类

3.注意事项

  • 加号 + 既可以进行数值之间的加法运算(‘a’、‘0’等字符型数据参与运算时,用该字符在计算机中所表示的数值进行运算),也可以用来拼接字符串(int转换为字符串,可采用“” + 1)

  • ++与—算数运算符,其放在变量前,先自增/减,再进行其他操作;其放在变量后,先进行其他操作,再自增/减

  • += 可避免进行强制类型转换

    short a =10;
    a += 20;
    a = (short)(a+20)   //直接a = a+20会报错
  • 三元运算符格式为:(关系表达式) ? 表达式1 : 表达式2

    // 使用三元运算符计算出三个整数中的最大值
    int a = 3;
    int b = 12;
    int c = -10;
    
    int d = (a>=b)?a:b;
    int e = (d>=c)?d:c;
    
    int f = ( ((a>=b)?a:b) >=c) ? ((a>=b)?a:b):c;

七、选择结构

1.if语句

if (表达式){
   // 具体语句 
}

if (表达式){
  // 具体语句 
}else{
   // 具体语句
}

if (表达式){
   // 具体语句 
}else if(表达式){
  // 具体语句 
} else{
   // 具体语句
}

2.switch语句

switch(表达式) {
	case1:
		语句体1;
		break;
	case2:
		语句体2;
		break;
	// ...
	default:
		语句体n+1;
		break;
}

八、循环结构

1.for 循环

for(int i = 0; i<10; i++){  // 初始化语句; 判断条件语句; 控制条件语句
    a = i;
}

2.while 循环

/*
初始化语句;
while(判断条件语句) {
	循环体语句;
	控制条件语句;
}
*/

int i = 0;
while(i<5){
    System.out.println(i);
    i++;
}

3.do…while 循环

初始化语句;
do {
	循环体语句;
	控制条件语句;
} while(判断条件语句);
  • while小括号后的分号不可省略
  • do…while循环的循环体语句至少执行1遍

4.死循环

for(::){}

while(true){}

5.break和continue

  • break中断,用于switch语句和循环语句: 在switch语句中,表示结束switch代码块 在循环语句中,表示结束循环
  • continue,结束本次循环,继续下次循环

九、方法

1.概念

方法,即函数,是完成特定功能的代码块。可以大大提高代码的复用性。

2.定义格式

修饰符 返回值类型 方法名(参数类型 参数名,参数类型 参数名...{
			方法体语句
			return 返回值;
}

3.注意事项

  • 要定义方法,必须明确返回值类型、方法名、参数类型

4.方法重载

在同一个类中的多个方法,他们的方法名相同,但参数列表不同(参数个数不同、对应位置的参数类型不同)。与返回值、修饰符均无关。

十、数组

1.概念

存储相同类型数据的容器。

数组和集合的区别???

2.定义格式

  • 数据类型 [ ] 数组名 = new 数据类型[长度] //数组长度在定义时指定,不可更改

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

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

int[] arr = new int[3];
int[] arr = new int[]{1,2,3};

int[] arr = {1,2,3};
// 可通过arr.length获取数组长度

3.访问与赋值

int[] arr = {1,2,3};
arr[2] = 555;

4.注意事项

避免越界、避免空指针;因此,需要提前检测数组长度、检测数组是否非空

十一、面向对象—封装

1.面向对象与面向过程

面向对象是将关注点放在实现某件事的人或某事物身上;面向过程是将注意力放在某件事物的具体实验步骤上面。

比如就洗衣服而言,面向对象可以是 用洗衣机洗衣服、干洗店洗衣服、让妈妈帮忙洗衣服等;面向过程就可以为 打水 -> 放衣服 -> 放洗衣粉 -> 揉搓 -> 晾晒等等。

面向对象思想特征:封装、继承、多态

2.类和对象

类是一个抽象概念,是某些属性和行为的集合;而对象是一种具体存在,是对该类事物的具体体现。

比如手机可以看做一个类,而一加6T就是对象。

3.类的定义与使用

通过类名.brand类名.model访问成员变量,通过类名.call()访问成员方法

4.成员变量与局部变量

  • 类中的变量叫做成员变量(方法外),方法中的变量叫做局部变量;
  • 两者的区别:
    • 作用定义位置不同(前者在类中、方法外,后者在方法中或者形式参数中),
    • 作用范围不同(前者在类中均有效,后者仅在方法中有效),
    • 初始化值不同(前者有默认初始化值整形为0,后者无初始化值从而必须先赋值再使用)
    • 内存中的位置和生命周期不同(前者存储于堆内存中,随着对象的创建而存在,随着对象的消失而消失;后者存储于栈内存中,随着方法的调用而存在,随着方法的调用完毕而消失)

5.this关键字

当成员变量和局部变量名称一致时,通过 this.名称 来指定成员变量

6.封装概念

将一系列事物共同的行为和属性提取出来,放到一个类或者方法中,隐藏实现细节,仅对外提供公共访问入口。比如setName和getName这种。

  • 提高安全性、复用性,将复杂的事情简单化(调用者不知道方法的具体实现;方法可以被重复使用;将繁多的代码以一个方法的方式呈现,仅通过调用方法就可以实现功能,代码维护也变得简单)
  • 封装的关键:绝对不能让类中的方法直接访问其它类的数据(属性),程序仅通过对象的方法与对象的数据进行交互

7.private关键字

私有的,用来修饰类的成员,被修饰的内容只能在本类中使用。(一般修饰成员变量)

public,公有的,被修饰的内容可以在任意类中使用;(一般修饰成员方法)

protected

8.构造方法

(1)概述

构造方法的作用为:初始化对象

格式:
修饰符 构造方法名(参数列表) {
//方法体
}
要求:

  • 构造方法名必须和类名相同(包括大小写)
  • 构造方法没有返回值(但是里边可以写return)

  • 构造方法没有返回值类型(连void都不能写)

注意:

  • 若未提供任何构造方法,系统会给出默认无参构造
  • 若已提供任何构造方法,系统不再提供无参构造
  • 构造方法可以重载(即支持同时无参构造和有参构造)

(2)分类

空参构造、有参构造

package cn.itcast.HomeWork;

public class Page {

    private int page;
    public Page() {}  // 无参构造

    public Page(int page){  // 有参构造,在new对象时需给定初始值
        this.page = page;
    }

    public int getPage(){
        return page;
    }

    public void nextPage(){
        this.page = this.page + 1;
    }

    public void prePage(){
        this.page = this.page - 1;
    }
}



public class testPage {
    public static void main(String[] args) {
        Page sc = new Page(1);  // 初始化类
        int cur_page = sc.getPage();
        System.out.println("当前页页码为:"+cur_page);
    }
}

十二、面向对象—继承

1.概念

通过扩展一个类来建立另外一个类的过程,叫做继承(inheritance)

所有的类都直接或间接的继承自: java.lang.Object ,被继承的类叫做父类(基类、超类), 继承的类叫做子类(派生类)

2.格式

class 父类 {
	// ...
}

class 子类 extends 父类 {
	// ...
}
  • 子类继承父类后,可拥有父类的非私有方法(成员变量、成员方法)
  • 构造方法用于初始化本类对象。

3.使用场景

(1)向上抽取

多个类中存在相同的属性和行为时,可以将这些内容提取出来放到一个新类中,让这些类和新类产生父子关系,实现 代码复用。

(2)向下拓展

当需要扩展已有的类的功能时,可以通过继承已有的类,在子类中添加新功能或重新实现已有功能,对父类(已有的类)没有影响。

4.优缺点

程序设计应该努力做到:低耦合、高内聚;耦合是指两个或者多个程序(或者模块)互相依赖

5.继承关系中类成员

(1)同名的成员变量

  • 查找变量的原则: 就近原则

    • 查找变量的顺序: 局部变量成员变量父类 → 更高的父类…Object
  • 访问父类变量的方式: super.父类变量名;

    • super: 当前对象父类的引用(父类内存空间的标识)
  • 对象初始化顺序: 先初始化父类内容,再初始化子类内容

(2)同名的成员方法

  • 查找方法的原则: 就近原则

  • 查找方法的顺序: 本类父类 → 更高的父类…Object

  • 访问父类方法的方式: super.父类方法名();

  • 定义重名方法的前提: 父类功不能完全满足现实需求,扩展父类功能;父类功能已过时,重新实现父类功能

6.构造

子类创建对象时,必须先初始化该对象的父类内容,若父类中不存在默认无参构造,须手动调用父类其它构造。

7.Java中继承的特点

  • Java仅支持类的单继承,不支持多继承。但支持多重(层)继承
  • Java支持接口的多继承
  • 构造方法用于初始化本类对象。因此无法继承构造方法
  • Java中子类仅可继承父类的非私有方法(成员变量、成员方法)。
  • 继承会体现一种“is a”的关系,如红富士是一种苹果,苹果是一种水果。
// 类的单继承
public class Fruit{
}
public class Apple extends Fruit{  // 类的单继承
}
public class Fuji extends Apple{  // 多重(层)继承
}

    
// 接口的多继承
// 接口A extends 接口B,接口C,接口D

8.方法重写(override)

常用于拓展父类功能,或者父类功能过时时,重写父类功能。但父类私有方法不能重写

十三、面向对象—多态

1.概述

多种状态,即同一对象在不同情况下,有不同的状态

2.实现步骤

  • 要有继承或者实现关系,
  • 子类重写父类方法

  • 父类引用 指向 子类对象

public class Test{
	public static void main(String args[]){
        // 父类引用Animal 指向 子类对象Dog
        Animal a = new Dog();
    }
}
  • 父类型变量作为参数时,可以接受任意子类型象
public static void main(String[] args){
    Dog dog = new Dog();
    dog.setName("布鲁斯");
    showAnimal(dog);   // 输出: 布鲁斯吃骨头
}

public static void showAnimal(Animal animal){  // 父类型变量作为参数
    animal.eat();
}
  • 多态关系中,成员变量无法重写调用成员变量, 遵循“编译看左,运行看左”

    • 编译看左: 意思是在编译期间会看左边的类型有没有这个成员, 没有就报错, 有就不报错.
    • 运行看左: 意思是在运行期间使用的是 左边的类型中的这个成员.
public static void main(String[] args){
    Animal a = new Dog();  // 多态
    Dog d = new Dog();
    System.out.println(a.name);   // 输出: Animal,编译看左,运行也看左
    System.out.println(d.name);   // 输出: Dog       
}

public class Animal{  // 父类型
    String name = "Animal";
}

public class Dog extends Animal{  // 子类型
    String name = "Dog";
}
  • 多态关系中,必然有成员方法是重写的。在调用成员方法时,遵循“编译看左,运行看右”
/*
     动物类案例:
         已知父类Animal, 成员变量为: 姓名, 成员方法为: eat()方法,一子类Dog类。
  */
public class Test {
    public static void main(String[] args) {
        //多态
        Animal an = new Dog();

        //测试成员方法的调用
        //  多态中调用成员方法:编译看左(左边的类型有没有这个成员)
        an.setName("哈士奇");  // 运行看右(运行时具体用的是右边类中的该成员).
        an.eat();  // 此处调用Dog类的eat方法
    }
}

3.优缺点

(1)增强可维护性。

基于继承关系,只需要维护父类代码,提高了代码的复用性,大大降低了维护程序的工作量。

封装:隐藏数据的实现细节,让数据的操作模块化,提高代码复用性
继承:复用方法,从对象的行为这个层面,提高代码的复用性
多态:复用对象,程序运行时同一个对象表现出不同的行为

不是很懂…

(2)增强可拓展性。

把不同的子类对象都当作父类看待,屏蔽了不同子类对象间的差异,做出通用的代码,以适应不同的需求,实现了向后兼容。

(3)多态无法使用子类特有成员,要使用需向下转型

  • 只能在继承层次内进行转换, 否则会报ClassCastException异常
  • 将父类对象转换成子类之前,使用instanceof进行检查:对象名 instanceof 数据类型
public class Test {
    public static void main(String[] args) {
        //需求: 通过多态创建对象, 调用子类中的成员.
        Animal an = new Dog();

        //调用eat()方法
        an.eat();

        //调用watch()方法, 属于子类独有的方法.
        //an.watch();
        //正确的写法
        /*Dog dog = (Dog)an;
        dog.watch();*/

        //不正常的转换.
        //Cat c = (Cat)an;

        //优化后的方案: 判断当前对象是否是Dog类的对象, 如果是, 再调用watch()方法.
        if(an instanceof Dog) { //判断an是否是Dog类的对象
            //能走到这里, 说明条件满足
            Dog dog = (Dog)an;
            dog.watch();
        }
    }
}
  • 向上转型(自动):子类型转换为父类型,如Animal animal = new Dog();

  • 向下转型(强制):父类型转换为子类型,如Dog dog = (Dog)animal;

十四、抽象类 abstract

1.概念

  • 只有方法声明,没有具体实现的方法称为抽象方法,用abstract修饰
  • 用abstract修饰,无法实例化的类称为抽象类。
修饰符 abstract class 类名 {}
修饰符 abstract 返回类型 方法名 {}

2.抽象类特点:

(1)抽象类与抽象方法辨析

  • 抽象类可以没有抽象方法,但是如果一个类被定义为抽象lei,即使其没有抽象方法,也不能被实例化,即无法直接构建一个该类的对象;
  • 如果一个类中包含抽象方法,则该类必须被定义为抽象类,用abstract修饰。

(2)抽象类无法直接被实例化

  • 抽象类是不具体的,没有方法体,即提供的成员无法生成一个具体对象。但一个不具体的对象是无法生成的。比如,我们可以实例化一个苹果(具体存在的),但无法实例化一个水果(一个抽象概念)
  • 从内存方面考虑的话,对象实例化时,关键词new会向JVM申请内存,这个类的成员(成员变量、成员方法)会被保存到内存中。而抽象类,没有具体的成员,因此无法准确分配内存。
  • 可以通过创建抽象类的非抽象子类来实例化对象。(子类重写了父类抽象类的所有方法)

(3)抽象类的子类

  • 如果是普通类,则必须重写父类所有抽象方法
  • 也 定义成抽象类,则不必全部重写。

4.抽象类成员的特点

  • 成员变量:可以有普通的成员变量,也可以有成员常量(final)
  • 成员方法:可以有普通方法,也可以有抽象方法。
  • 构造方法:像普通类一样有构造方法,且可以重载

5.final 关键字

意思为最终的,即被修饰的元素无法被修改

  • 修饰类时,该类无法被继承
  • 修饰方法时,该方法不能被重写无法与abstract共存(抽象方法必然被重写)

  • 修饰变量时,变为常量,只能赋值一次。

6.static 关键字

静态的,用来修饰类、成员方法、成员变量

  • 被static修饰的成员变量,称为静态变量(类变量),被本类所有对象所共享。可以通过类名.成员变量名的形式直接访问
  • 成员变量包括实例变量、类变量(静态变量)
  • 被static修饰的成员方法,称为静态方法

    • 没有对象this
    • 无法访问非静态成员;但非静态方法中,是可以访问静态成员方法/变量的。
    • 无法被声明为abstract
    • 通过类名.成员方法名(参数)调用。
    • 静态方法的运行优先级高于main,也高于构造方法。
  • 执行顺序:

    父类静态代码块 ->子类静态代码块 ->父类非静态代码块 -> 父类构造函数 -> 子类非静态代码块 -> 子类构造函数。

十五、接口

1.接口定义

接口用来提供统一的规则、规范

interface 接口名 {    
}

// 接口和类是实现关系,用implement表示
class 类名 implements 接口名{
}

2.接口的特点

(1)接口没有成员变量,默认有public static final修饰,即只有成员常量

(2)接口中的成员方法默认都有public abstract修饰,因此接口是抽象的,无法实例化,但可以通过多态的方式实例化子类对象

public abstract 返回值类型 方法名(){}

// JDK8之后,可以有默认方法和静态方法
public default 返回值类型 方法名(){}
static 返回值类型 方法名() {}

// JDK9之后,可以有私有方法
private 返回值类型 方法名(){}

(3)接口没有构造方法(接口无成员变量–都是常量,无需要初始化的具体方法–抽象的,因此无需要初始化的成员)

(4)接口的实现类 要重写接口的所有抽象方法,或者 声明为抽象类

(5)接口直接可以多继承。(类只能单继承,但可以多重/层继承)

interface 接口1 extends 接口2,接口3,接口4...{ }

十六、常用API

1.Object类

// 类层次结构中最顶层的类,所有类都直接或者间接的继承自Object类

// hashCode() 返回该对象的哈希码值(通过对象地址值计算得到)
// toString() 返回该对象的字符串表示
// equals()   返回其他某个对象是否与此对象相等(比较地址值),必须重写

2.Scanner类

import java.util.Scanner;
// 创建对象
Scanner sc = new Scanner(System.in);

// 接受字符串类型的数据(以 换行符 作为分隔)
String s1 = sc.nextLine();
// 接受字符串类型的数据(以 空白字符 作为分隔,如空格,tab,回车等)
String s2 = sc.next();

// 判断是否有下一个输入项
if (sc.hasNextInt()){
    int num = sc.nextInt();
}

3.Random类

import java.util.Random;

Random r = new Random();
int num = r.nextInt(10);  // 获取0~9范围内的随机数
int num2 = r.nextInt(100) + 1; //获取1~100范围内的随机数

4.String类

/**
	定义字符串
*/
// 将 指定的字节数组 转换为 字符串
byte[] bys = {1,2,3};
String s1 = new String(bys);
// 将 制定的字符数组 转换为 字符串
String[] chs = {'a','b','c'};
String s2 = new String(chs);
String s3 = "abc";
String s4 = "" + 0;

// 判断两个字符串是否相同,区分大小写
boolean b1 = str1.equals(str2);
// 不区分大小写
boolean b2 = str1.equalsIgnoreCase(str2);

// 判断是否以给定字符串开头
boolean b3 = str1.startwith("abc");

// 判断字符串是否为空
boolean b4 = str1.isEmpty();

// 获取当前字符串的长度
int len = str1.length();

// 获取指定字符(串)第一次出现的索引
int index1 = str1.indexOf('a');
// 获取指定字符(串)最后一次次出现的索引
int index2 = str1.lastIndexOf('a');

// 获取指定索引位置处的字符
char ch = str1.charAt(1);
// 获取指定索引位置(含)之后的字符串
String s5 = str.substring(5);
// 获取从索引start位置(含)起至索引end位置(不含)的字符串 
String s6 = str.substring(5,10);


/*
	String类 的转换功能
*/
String s1 = "abc";

// 1.将 字符串 转换成 字节数组
byte[] bys = s1.getBytes();    //97, 98, 99。直接打印数组的话,显示的是地址值

// 2.将 字符串 转换成 字符数组
char[] chs = s1.toCharArray();  //'a','b','c'

// 3.将指定类型数据转换成字符串
String s2 = String.valueOf(123); // "123"
String s3 = "" + 123;  // 更常用一些

// 4.将指定字符(串)替换成新的字符(串)
String s4 = "abc abc abc";
String s5 = s4.replace('b','d');  //'d'  替换 'b'

// 5.切割字符串,返回切割后的字符串数据,原字符串不变
String[] arr = s4.split(" ");

// 6.去掉字符串两端的空白字符
String s6 = "  a   b   c   ";
String s7 = s6.trim();

5.StringBuilder类

/*
    StringBuilder:
        简介
            可变字符序列,用于构造字符串对象。内部使用『自动扩容的数组』操作字符串数据。
            传统的String无法多次修改。
        构造方法
            StringBuilder():        构造一个空的StringBuilder容器
            StringBuilder(String):  构造一个StringBuilder容器,并添加指定字符串
        成员方法
            StringBuilder append(…):    将任意数据添加到StringBuilder容器中, 返回自身
            String  toString():         将 当前StringBuilder容器 转成 字符串
*/
public class Test {
    public static void main(String[] args) {
        // 1.空参构造
        StringBuilder sb = new StringBuilder();
        StringBuilder sb2 = sb.append("abc");

        // 2.带参构造
        StringBuilder sb3 = new StringBuilder("abc");

        //需求: 将三个字符串拼接成一个新的字符串:  学Java, 到传智播客 找小黑!
        StringBuilder sb4 = new StringBuilder();
        sb4.append("学Java,");
        sb4.append("到传智播客");
        sb4.append("找小黑!");
        System.out.println("sb4: " + sb4);
        System.out.println("----------------");
        String s = sb4.toString(); // 转换为字符串
        System.out.println("字符串s: " + s);
    }
}

6.Date类

/*
        简介:
            日期类,用于操作时间相关信息。
        构造方法:
            Date():     构造一个日期对象,当前系统时间,精确到毫秒
            Date(long): 构造一个日期对象,时间为自“1970年1月1日00:00:00 GMT”起,至指定参数的毫秒数
        成员方法:
            long getTime(): 将日期对象转换成对应时间的毫秒值
*/

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        //测试空参构造, 采用当前操作系统的默认时间
        Date date1 = new Date();
        System.out.println("date1:" + date1);

        //获取当前操作系统时间的毫秒值
        long time = date1.getTime();
        System.out.println("time:" + time);

        //Sun Jun 06 17:04:39 CST 2066   --> 3043040679456
        //创建一个指定的时间
        Date date2 = new Date(3043040679456L);
        System.out.println("date2:" + date2);
    }
}

7.Calender类

/*
    Calendar类:
        简介:
            日历类. 用于操作日期相关信息。
        成员方法
            static Calendar getInstance(): 根据当前系统时区和语言环境获取日历对象
            int get(int field):            返回给定日历字段的值
            void set(int field, int value):将给定的日历字段设置为指定的值
 */
 
import java.util.Calendar;

public class Test2 {
    public static void main(String[] args) {
        // 创建Calendar类型的对象.
        Calendar c = Calendar.getInstance();

        // 获取年月日的信息
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);  //Java中使用0-11的数字表示月份的, 对应1-12月
        int day = c.get(Calendar.DATE);

        // 设置指定时间为: 2022年2月2日
        c.set(2022, 1, 2);
    }
}

8.SimpleDateFormat类

/*
	SimpleDateFormat类
		构造:
 */

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

public class Demo2 {
    public static void main(String[] args) throws ParseException {
        Date date = new Date();
        System.out.println("初始日期格式为:" + date);

        SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("第一种时间格式为:" + dtf.format(date));
        
        SimpleDateFormat dtf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("第二种时间格式为:" + dtf2.format(date));
        
        SimpleDateFormat dtf3 = new SimpleDateFormat("yyyy年MM月dd日");
        System.out.println("第三种时间格式为:" + dtf3.format(date));
        System.out.println();


        String str = "2020-03-18 12:00";
        SimpleDateFormat dtf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date newDate = dtf4.parse(str);  // 将字符串解析为时间格式
        System.out.println(str + " -----> " + dtf3.format(newDate));


        DateUtil("12",date);
    }

    public static void DateUtil(String args, Date date){

        SimpleDateFormat dtf1 = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat dtf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat dtf3 = new SimpleDateFormat("yyyy年MM月dd日");
        if(args == "12"){
            System.out.println(date + " -----> " + dtf3.format(date));
        }
    }
}

十七、集合

1.概念

集合是指用来存储多个元素的容器。相比于固定长度的int[]这种类型的数组,其可以任意扩容

单列集合和双列集合

(1)单列集合CollectionListSet两个常用接口,其中List的常用实现类为ArrayList,元素有序且可重复;Set的常用实现类为HashSet,元素无序且不可重复;

(2)双列集合Map的实现类为HashMap,其元素由键值对 key--value构成,其中key不可重复,value可重复。

2.单列集合之List集合

  • 元素有序(元素的存取顺序一致),可重复。
  • 通过创建其子类ArrayList对象来完成List接口的实例化。List<> list = new ArrayList<>();
  • List接口中的成员方法:
    • public boolean add(E e); // 将数据添加到集合的末尾, 这里的E是泛型的意思, 目前可以先理解为Object类型.
    • public E get(int index); // 根据索引, 索取其对应的元素.
    • public int size(); // 获取集合的长度.
import java.util.ArrayList;
import java.util.List;
       
public class Test {
    public static void main(String[] args) {
        //1. 创建集合对象.
        List list = new ArrayList();
        //2. 创建元素对象.
        Student s1 = new Student("乔峰",41);
        Student s2 = new Student("乔峰",41);
        Student s3 = new Student("虚竹",38);
        Student s4 = new Student("段誉",26);
        //3. 将元素对象添加到集合对象中.
        /*boolean b1 = list.add(s1);
        System.out.println(b1);
		*/
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);

        //直接打印集合
        System.out.println(list);

        //4. 遍历集合. 通过size()获取集合长度
        for (int i = 0; i < list.size(); i++) {
            Object obj2 = list.get(i);
            System.out.println("索引为 " + i + "的元素是: " + obj2 );
        }
    }
}

3.增强for循环

/*
    增强for格式:
        for(元素的数据类型 变量名 : 要遍历的数组或者集合对象) {
            //循环体, 变量也就是元素
        }
    快捷方式:
        iter --> 回车
    注意:
        增强for的底层依赖的是迭代器(Iterator).
        大白话解释: 增强for就是迭代器的简写形式.
 */

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        //1. 创建集合对象.
        List list = new ArrayList();
        //2. 将元素对象添加到集合对象中.
        list.add(10);
        list.add(10);  // ArrayList中元素可重复
        list.add(30);
        list.add(20);
        list.add(40);

        //3. 遍历集合.
        for(Object obj : list) {
            // obj是集合中的元素, 其本身应该是Integer类型的数据.
            Integer ii = (Integer)obj;
            System.out.println(ii);
        }
    }
}

4.迭代器

迭代器是遍历collection集合的通用形式。

(1)常用方法

  • next():返回迭代的下一个元素对象
  • hasNext():如果仍有元素可以迭代,则返回true

(2)使用步骤

  • 根据集合对象获取其迭代器对象

  • 判断迭代器是否仍有元素

  • 如果有就获取元素
Iterator it = list.iterator();
while(it.hasNext()) { 
      String s = (String)it.next();
      System.out.println(s);
}

(3)分类

  • 普通的迭代器iterator在遍历集合的同时不能添加或者删除元素, 否则会报: 并发修改异常.
  • 列表迭代器listIterator(List体系独有)在遍历集合的同时,还可以修改集合中的元素(添加, 删除等),,但必须使用列表迭代器中的方法.
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Test1 {
    public static void main(String[] args) {
        //需求: 测试列表迭代器
        // 1. 创建集合对象.
        List list = new ArrayList();
        // 2. 将元素对象添加到集合对象中.
        list.add("a");
        list.add("b");
        list.add("c");
        
        //需求: 判断集合中如果有字符串"b", 就在其后边添加一个新的字符串: java
        //1. 根据集合对象获取列表迭代器对象.
        ListIterator lit = list.listIterator(); // 列表迭代器
        //2. 判断迭代器中是否有元素.
        while(lit.hasNext()) {
            //3. 有就获取元素即可.
            String s = (String)lit.next();
            if ("b".equals(s)) {
                //list.add("java");  //这样写不行, 必须调用列表迭代器的方法来实现.
                lit.add("java");   // 重点!!
            }
        }
        //打印新的集合中的值
        System.out.println(list);
    }
}

5.泛型

泛型,即泛指任意类型,又叫参数化类型(Parameterized Type),对具体类型的使用起到辅助作用,类似于方法的参数。

  • 比如:List<String> list2 = new ArrayList<>();

  • 集合类泛型,表示该集合中存放指定类型的元素

  • 优点:保证类型安全,也避免了类型的转换
public class Test {
    public static void main(String[] args) {
        //不使用泛型的集合
        List list1 = new ArrayList();
        list1.add("a");
        list1.add("b");
        list1.add("c");
        System.out.println(list1);
        //list1.add(10);  会报类型转换异常(ClassCastException)

        for (Object obj : list1) {   // 增强for里面为Object类型数据
            String s = (String)obj;  // 需进行类型转换
            System.out.println(s);
        }
        System.out.println("----------------");

        //需求: 演示泛型
        //1. 创建集合对象.
       List<String> list2 = new ArrayList<>();
        list2.add("abc");
        list2.add("bcd");
        list2.add("cde");

        for (String s : list2) {   // 增强for里面直接为String型数据了
            System.out.println(s); // 无需进行类型转换
        }
    }
}

6.Collections工具类

  • Collections.max(集合名):返回该集合的最大元素
  • Collections.sort(集合名):对集合进行排序(默认升序)
  • Collections.reverse(集合名):反正集合元素,搭配sort可实现降序排列
  • Collections.shuffle(集合名):随机打乱集合元素

7.单列集合之Set集合

  • Set集合中元素不可重复、无序

  • 创建集合对象:Set<T> set = new HashSet<>();

  • 添加元素:set.add("1");

  • 遍历集合:

    // 迭代器
    Iterator<Student> it = set.iterator();
    while(it.hasNext()){
        Student s = it.next();
        System.out.println(s);
    }
    
    // 增强for
    for(Student student: set){
        System.out.println(student);
    }
  • Set集合保证元素的唯一性依赖: equals()hashCode()两个方法。默认调用的是Object类中的equals方法(比较地址值是否相同),因此需要重写equals和hashCode方法!!

8.双列集合之Map集合

  • 双列集合,元素由键值对(Entry)构成:keyvalue

    • key不可以重复,但value可以重复。
  • 创建对象:Map<T1, T2> map = new HashMap<>();

  • T1和T2分别表示key和value的数据类型

  • 添加元素:map.put(key,value);

    • 元素第一次添加,会返回null;重复添加时,新值覆盖旧值,并返回旧值
  • 根据key获取其对应的value:map.get(key);

  • 遍历集合:

    // 增强for
    Set<Interger> keys = map.keySet();  // 获取所有的键
    for(Interger key: keys){
        Student val = map.get(key);    // 根据key获取其对应的value
        System.out.println(val);
    }
    
    // 迭代器
    Set<Interger> keys = map.keySet();  // 获取所有的键
    Iterator it = keys.iterator();
    while(it.hasNext()){
        Integer key = it.next();      // 获取迭代器的数据
        Student val = map.get(key);   // 根据key获取其对应的value
        System.out.println(val);
    }

十八、异常

1.概念

异常,即非正常的情况,也就是程序出现的错误

2.分类

顶层父类: Throwable

  • 异常(Exception),如NullPointerException
  • 错误(Error),如StackOverFlowError

3.处理方式

  • 捕获异常,自己处理 try..catch..finally

    • finally代码块可以省略,但不能和catch部分同时省略;
    • finally代码之前若有return语句,先执行return语句再执行finally代码块,最后返回return的结果
    • 方法重写时,子类方法不能抛出比父类方法更大的日常。
    try {
    	// 尝试执行的代码
    } catch(Exception e) {
    	// 出现可能的异常之后的处理代码
    } finally {
    	// 一定会执行的代码,如关闭资源
    }
    
    // 多个异常分别处理
    try {
    	// 尝试执行的代码
    } catch(异常A e) {
    	// 出现可能的异常之后的处理代码
    } catch(异常B e) {
    	// 出现可能的异常之后的处理代码
    } finally {
    	// 一定会执行的代码,如关闭资源
    }
  • 抛出, 交给调用者处理. throws

    public void 方法名() throws Exceptoin {
    }
    
    // 调用该方法的调用者必须处理这个异常,try..catch..捕获,或者 继续抛出日常

十九、IO流

1.概念

输入、输出流,可用于在本地磁盘、网络上读写数据

2.分类

(1)字符流:按字符(a~z, 0~9)读写数据

(2)字节流:按字节(0,1)读写数据

3.File类

(1)概念

一个File对象代表磁盘上的某个文件或者文件夹

(2)构造方法

// 1.根据给定的字符串路径创建其对应File对象:File(String pathname)  
File file1 = new File("D:/abc/1.txt");

// 2.根据给定的字符串形式的父目录和子文件(夹)名创建File对象:File(String parent, String child)   
File file2 = new File("D:/abc/", "1.txt");

// 3.根据给定的父目录对象和子文件(夹)名创建File对象:File(File parent, String child)     
File file3 = new File("D:/abc/");
File file4 = new File(file3, "1.txt");

(3)成员方法

  • 创建功能:
    • createNewFile():创建文件
    • mkdir()mkdirs():创建单级、多级目录
  • 判断功能:
    • isDirectory():判断File对象是否为目录
    • isFile():判断File对象是否为文件
    • exists():判断File对象是否存在
  • 获取路径、名称等:
    • getAbsolutePath():获取绝对路径
      • 从本地磁盘开始的路径,如C:\Users\itcast\Desktop
    • getPath():获取文件的相对路径
      • Java项目中,相对路径从项目名开始
    • getName():获取文件名
    • list():获取指定目录下所有文件(夹)名称数组
    • listFiles():获取指定目录下所有文件(夹)File数组

4.字符流读写文件

以字符为单位来操作数据。如纯文本文件

Reader: 字符输入流的顶层抽象类.
FileReader: 普通的字符输入流.
BufferedReader: 高效的字符输入流(也叫: 字符缓冲输入流)

Writer: 字符输出流的顶层抽象类.
FileWriter: 普通的字符输出流.
BufferedWriter: 高效的字符输出流(也叫: 字符缓冲输出流)

(1)读

步骤:创建文件对象,读入数据,关闭资源

  • 按单个字符读取:read()

    • 返回读到的字符
  • 按字符数组读取:read(char[] chs)

    • 返回读取到的有效字符数,读不到返回-1.
// 创建普通字符输入流
Reader reader = new FileReader("lib/1.txt");
int ch;   // 接收读到的单字符
while((ch = reader.read()) != -1){  // 三步:读取字符,赋值,判断
   System.out.println(ch);
}
reader.close();  // 释放资源.


Reader reader = new FileReader("lib/2.txt");
char[] chs = new char[3];
int len;
while((len = reader.read(chs)) != -1) {   //  三步:读取字符存到chs中,返回值赋给len,比较
    String s = new String(chs,0,len);    // 0: 表示起始索引;len: 表示要操作的字符的个数.
    System.out.println(s);
}
reader.close();

(2)写

步骤:创建文件对象、写入数据、关闭资源

  • 一次写一个字符:write(char)
  • 一次写一个指定的字符数组:write(char[] chs, int index, int len)
  • 一次写一个字符串
Writer w = new FileWrite("lib/1.txt");  // 创建对象

w.write('好');
w.write("你好!");
char[] chs = {'你','好','!'};
w.write(chs,0,2); // 索引从0开始,后面两个字符

w.close();  // 关闭资源

(3)拷贝文件

步骤:创建字符流读、写文件对象,读取数据,写入数据,关闭资源

// 一个字符一个字符的来
FileReader fr = new FileReader("lib/1.txt");
FileWriter fw = new FileWriter("lib/3.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
int len;
while((len = fr.read()) != -1) {
     fw.write(len);
}
fr.close();
fw.close();

// 一组一组的来
FileReader fr = new FileReader("lib/1.txt");
FileWriter fw = new FileWriter("lib/3.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1) {
     fw.write(chs,0,len);
}
fr.close();
fw.close();
  • 字符缓冲流拷贝文件
// 创建字符缓冲对象,并关联响应文件
BufferedReader br = new BufferedReader(new FileReader("lib/1.txt"));  // 输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("lib/2.txt"));  // 输出流

// 一次读取一个字符,读不到返回-1
int len;
while((len = br.read()) != -1){
    bw.write();
}
br.close();   // 关闭的是br和bw
bw.close();


// 创建字符缓冲对象,并关联响应文件
BufferedReader br = new BufferedReader(new FileReader("lib/1.txt"));  // 输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("lib/2.txt"));  // 输出流

// readLine()一次读取一行数据,返回读到的内容,读不到时返回null
String str;
while((str = br.readLine()) != null){
    bw.write(str);
    bw.newLine(); // !!要换行,在linux中相当于\n,windows中相当于\r\n,mac中为\r
}
br.close();   // 关闭的是br和bw
bw.close();

5.字节流读写文件

以字节为单位来操作数据。如音视频文件

InputStream: 字节输入流的顶层抽象类.
FileInputStream: 普通的字节输入流.
BufferedInputStream: 高效的字节输入流(也叫: 字节缓冲输入流)

OutputStream: 字节输出流的顶层抽象类.
FileOutputStream: 普通的字节输出流.
BufferedOutputStream: 高效的字节输出流(也叫: 字节缓冲输出流).

和字符流差不多,比着来就好

// 创建 普通字节流读文件对象、普通字节流写文件对象:
InputStream is = new FileInputStream("Desktop.jpg");
OutputStream os = new FileOutputStream("D:\\博学谷桌面.jpg");

// 创建 缓冲字节流读文件对象、缓冲字节流写文件对象:
BufferedInputStream bi = new BufferedInputStream(new FileInputStream("Desktop.jpg"));
BufferedOutputStream bi = new BufferedOutputStream(new FileOutputStream("D:\\博学谷桌面.jpg"));

// 定义字节数组,每次读取2048个字节
byte[] b = new byte[1024];
int len;
while((len = is.read(b)) != -1){
    os.write(b, 0, len);
}
is.close();
os.close();

二十、反射

二十一、多线程

(1)定义

进程是指正在运行的程序,而线程是指进程中的执行路径

  • 单线程:一个进程只有一条执行路径。比如记事本
  • 多线程:一个进程有多条执行路径。比如扫雷

(2)实现方式

  • 继承 Threads类
    • 定义一个类MyThread 继承Thread类
  • 在 MyThread类中重写run()方法

    • run方法用来封装被线程执行的代码。
    • start()方法:启动线程,然后由JVM调用此线程的run()方法
      • 创建 MyThread类的对象
      • 启动线程
  • 实现Runnable接口

    • 定义一个类 MyRunnable实现 Runnable接口
    • 在 MyRunnable类中重写run()方法(无法直接使用getName方法)
    • 创建 Myrunnable类的对象
    • 创建 Thread类的对象,把 Myrunnable对象作为构造方法的参数
    • 启动线程

  • 相比继承 Thread类,实现 Runnable接口的好处

    • 避兔了Java单继承的局限性
    • 适合多个相同程序的代码去处理同个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想

(3)设置和获取线程名称

  • 通过setName设置线程名称,通过getName获取线程名称

  • 通过带参构造设置线程名称,但需要在父类中重新定义带参构造

  • 也可通过cuurentthread方法,先得到线程对象再获取当前线程名称

(4)线程调度

目前主要有两种线程调度模型:

  • 分时调度模型:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间片
  • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择个,优先级高的线程 获取的CPU时间片相对多一些

Java采用的是,抢占式调度模型。线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多或者多次运行的时候才能看到你想要的效果

  • 通过setPriority(int newPriority)设置线程优先级(默认为5,最小值为1,最大10)

  • 通过getPriority()获取线程优先级

(5)线程控制

  • join:等待这个线程死亡

  • sleep:使当前线程暂停n毫秒

  • setDaemon:将此线程标记为守护线程()

(6)线程的生命周期

(7)示例:卖票

  • 需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计个程序模拟该电影院卖票

  • 思路:

(8)synchronized解决数据安全问题

  • 同步代码块

同步方法时,直接将synchronized关键字加到方法名上即可。

  • 同步方法的锁对象是this,但静态方法是木得this的所以要通过类名.class的方法进行上锁。
  • 修饰符 (static) synchronized 返回值类型 方法名(方法参数) { }

线程安全的类有:

  • StringBuffer线程安全、可变的字符序列。从版本JDK5开始,被 StringBuilder替代。通常应该使用 StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步。

  • Vector:从Java2平台v1.2开始,该类改进了List接口,使其成为 Java Collections Framework的成员。与新的集合实现不同, Vector被同步。如果不需要线程安全的实现,建议使用 Arraylist代替 Vector

  • Hashtable: 该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值。从Java2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为 Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。如果不需要线程安全的实现,建议使用 Hashmap代替 Hashtable

多线程环境下,为了满足安全性需求,采用StringBuffer替代StringBuilder,但线程安全的vector和hashtable并不常用,常用的是Collections里的synchronizedListsynchronizedMap

(9)Lock锁

(10)生产者/消费者模式

二十二、网络编程

(1)基础概念

三要素:IP地址、端口、协议:

(2)IP地址

  • IPV4:32位(bit)地址,4个字节,二进制,如1000000101010000000000101000010,长表示为十进制形式,即192.168.1.66
  • IPV6:128位地址,每16位一组,组成8组16进制数。

InetAddress类,表示Internet协议(IP)地址

  • getByName(String host):确定主机名称的IP地址
  • getHostName():获取此IP地址的主机名
  • getHostAddress():获取文本显示中的IP地址字符串

(3)端口

  • 端口:设备上应用程序的唯一标识
  • 端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败、

(4)协议

  • UDP协议:用户数据报协议,面向无连接,无法保证数据的完整性;但资源消耗少,通信效率高
  • TCP协议:传输控制协议(Transmission Control Protocol),面向连接,可靠无差错,三次握手。
    • 第1次握手,客户端向服务器端发出连接请求,等待服务器确认
    • 第2次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
    • 第3次握手,客户端再次向服务器端发送确认信息,确认连接

🚣‍♀️ UDP发送数据

  • 创建发送端的Socket对象:DatagramSocket ds = new DatagramSocket();
  • 创建数据,并把数据打包:
    • byte[] bys = "Hello,UDP".getBytes();
    • InetAddress address = InetAddress.getByName("192.168.1.66");
    • DatagramPacket dp = new DatagramPacket(bys, bys.length, address, 10086);
  • 调用DatagramSocket对象的方法发送数据:ds.send(dp);
  • 关闭客户端:ds.close();

🚣‍♀️ UDP接收数据

  • 创建接收端的 Socket对象:DatagramSocket ds = new DatagramSocket(10086); // 构建数据报套接字,并将其绑定到本地主机上的特定端口上
  • 创建一个数据包,用于接收数据:
    • byte[] bys = new byte[1024];
    • DatagramPacket dp = new DatagramPacket(bys, bys.length);
  • 调用 Datagramsocket对象的方法接收数据:ds.receive(dp);
  • 解析数据包,并把数据在控制台显示:
    • byte[] datas = dp.getData();
    • String dataString = new String(datas, 0, dp.getLength());
    • System.out.println(dataString);
  • 关闭接收端:ds.close();

🚣‍♀️UDP练习

// SendDemo.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SendDemo {
    public static void main(String[] args) throws IOException {
        // 创建发送端的DataSocket对象
        DatagramSocket ds = new DatagramSocket();

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ( (line= br.readLine())!=null ) {
            if("886".equals(line)){
                break;
            }

            // 创建数据,并打包(地址为本机ip地址,注意修改)
            byte[] bys = line.getBytes();
            DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("172.18.154.202"), 12345);

            ds.send(dp);
        }

        // 关闭客户端
        ds.close();
    }
}


// ReceiveDemo.java,支持开启多个发送界面,该接收端均能收到
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        // 创建接收端的Socket对象
        DatagramSocket ds = new DatagramSocket(12345);

        while(true){
            // 创建数据包,用于接收数据
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);

            // 调用DatagramSocket对象的方法接收数据包
            ds.receive(dp);

            // 解析数据包,并显示
            System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
        }
    }
}

📟 TCP发送数据

  • 创建客户端的Socket对象:Socket s = new Socket(“172.18.154.202”, 10000);
  • 获取输出流,写数据:OutputStream os = s.getOutputStream(); os.write("hello, TCP".getBytes());
  • 关闭对象:s.close();

📟 TCP接收数据

  • 创建服务器端的 Socket对象( ServerSocket):ServerSocket ss = new ServerSocket(10000);
  • 监听要连接的套接字,返回一个Socket对象:Socket s = ss.accept();
  • 获取输入流,读数据,并把数据显示在控制台:
    • InputStream is = s.getInputStream();
    • byte[] bys = new byte[1024];
    • String data = new String(bys, 0, is.read(bys));
  • 释放资源:s.closer(); ss.close();

客户端,先写数据,后读数据;服务器端,先读数据,后写数据。

二十三、lambda表达式

(1)组成三要素

形式参数、箭头、代码块

(2)使用前提

  • 有一个接口,且接口中有且只有一个抽象方法(接口中的方法默认有public abstract)

(3)练习

(4)省略模式

  • 参数类型可以省略,但是有多个参数时,要一省全省。
  • 如果参数有且仅有一个,小括号也可以省略。
  • 如果代码块中的语句只有一条,可以省略大括号和分号。(如果有return,return也要省略掉)

(5)注意事项

  • 使用lambda必须有接口,且接口中有且只有一个抽象方法
  • 必须有上下文环境,才能推导出lambda对应的接口

(6)和匿名内部类的区别

  • 所需类型不同:
    • 匿名内部类:可以是接口、抽象类、具体类
    • Lambda表达式:只能是接口
  • 使用限制不同:

    • 如果接口中有且只有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类;
    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式。
  • 实现原理不同:

    • 匿名内部类:编译之后,产生一个单独的.class字节码文件
    • Lambda表达式:编译之后,无单独字节码文件生成,对应的字节码会在运行时动态生成。

二十四、Stream流

其他

1. Java中有没有真正意义的引用传递?

  • 值传递( pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
  • 引用传递( pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数

2.访问权限修饰符


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!