Java基础学习操超长6w字笔记

JAVA

MS-DOS操作(命令提示符(cmd))

启动:按win+R键 输入cmd

切换盘符:在C/D:+cd+要进入的文件夹(可以一个一个进入)

​ cd…(返回上一级)(cd\进入根目录)

​ dir(显示文件夹里的文件)

​ cls( 清空面板 )

​ exit(退出)

JVM,JRE,JDK

java虚拟机,java都运行于jvm

java程序在不同的系统的JVM上通用,但是JVM虚拟机是不一样的

JRE 核心类库

JDK开发用工具包

JAVA编译

javac.exe:编译器

java.exe:解释器

编译

记事本,改为.java后运行

public class Hello World//这个蓝色的要和文件名一样
{
    public static void main(String[] args)
    {
        System.out.println("Hello , World!");
    }
}

运行

1.输入.java所在的位置

2.输入Javac 文件名.java,创建一个class文件

3.输入java 文件名

注释

依旧是//注释法

还有/* -------- */

注意的关键字

1.完全小写的关键字

2.编译器有提示的关键字

语法

标识符

可以用大小写,数字, , 和 进 行 命 名 ( 比 c 多 了 个 ,和_进行命名(比c多了个 c

常量

程序运行期间不变的量

1.字符串常量(用""引起来的)

System.out.println("ABC");

2.整数常量

System.out.println(123);

3.浮点数常量

System.out.println(3.14);

4.字符常量(‘’)(只能有一个字符)

System.out.println('a');//里面有且只能有一个字符,不能为空

5.布尔常量(true/false)对与错

System.out.println(true);
System.out.println(false);

6.空常量(null)

基本数据类型

整型

byte ,short ,long ,int

浮点

float,double

字符

char

布尔

boolean

注意事项

1.浮点可能只是近似值

2.浮点数默认是double,要使用float 要加上后缀F/f,整数默认int,要使用long类型要加上后缀 l/L

3.用byte/short的时候,数据值不能超过范围

4.不能使用未赋值的变量

5.定义的变量只能在定义的{}内使用

变量

格式:数据类型 变量名称;

赋值:变量名称=数据值;

也可以:数据类型 变量名称=数据值;

public class newsb
{
	public static void main(String[] args)
	{
		int num1;
		num1=10;
		System.out.println(num1);
		num1=20;
		System.out.println(num1);
		
		int num2=30;
		System.out.println(num2);
		
		long num3=300000000L;
		System.out.println(num3);
		
		float num4=3.14F;
		System.out.println(num4);
		
		char zifu='A';
		System.out.println(zifu);
		
		boolean var=true;
		System.out.println(var);
		var=false;
		System.out.println(var);
	}
}

局部变量

定义在方法内部,且没有默认值

只能在方法中使用

成员变量

在方法的外部,直接写在类中,自带默认值

整个类当中都可以使用

数据类型转换

自动转换
public class HE
{
	public static void main(String[] args)
	{
		System.out.println(1024);
		System.out.println(3.14);
		
		long num1=100;//出现了强制类型转换,没有加L后缀,所以是把int赋值到long中
		System.out.println(num1);
		}
}

规制:数据类型从小到大(字节数从少到多)

强制转化

范围小的变量名=(范围小的类型)原来范围大的数据

		int num1=(int)100L;
		System.out.println(num1);

记得 48 -‘0’

​ 65-‘A’

​ 97-‘a’

运算符

大部分类似c,在不同的数据类型计算时,结果取范围大的那种

字符串使用(任何数据类型和字符串类型进行连接时,结果都会变成字符串)

public static void main(String[] args)
	{
		String str="泽先生";
		System.out.println(str);
    
    	System.out.println("林"+"先生");
	}

复合运算符

a(运算符)= b–>a=a(运算符)b

逻辑运算符

||和&&有短路效果,如果左边已经得到最后结果,那么右边的语句不会执行

	public static void main(String[] args)
	{
		int a=10;
		System.out.println(a==11&&++a>1);
		System.out.println(a);//10              
	}

方法

定义

public static void 方法名称()

{

​ 方法体

}(要在main前的那个{}内定义)

修饰符(public static) 返回值类型 方法名(参数类型 参数名)

{

方法体;

return 返回值;

}
调用

方法名称();

方法的地址传递调用
public static void printarray(int[] array){
    for(i=0;i<array.length;i++)
    {
        System.out.println(array[i]);
    }
}
数组的返回
public static int[] calculate(int a,int b,int c)
{
    int sum=a+b+c;
    int x=a-b-c;
    int[] array={sum,x};
    return array;
}

方法重载

多个方法的名称相同,但是参数列表不一样,参数的类型,顺序可以不一样

由参数的对应来调用方法

但是不能通过修改参数名称来重载

也不能修改方法的类型来重载

构造方法

1.构造方法的名称要和类名称完全一样,大小写也要一样

2.构造方法不要写返回值类型,void也不写

3.构造方法无return返回值

4.就算我将public Student()后面的内容散去,也能在另外的java文件中用

new Student();调用,只不过没有产生任何的作用罢了

5.构造方法也能用重载的方法调用

public class Student {
    public Student()
    {
        System.out.println("开始学习构造方法拉");
    }
    public Student(String name,int age)
    {
        
    }
}

jshell使用

在cmd中输入jshell进入

退出/exit

选择语句

switch(a)
{
    case 1:.....;break;
    case 2:.....;break;
    default:....;break;
}

注意 a只能放入byte/short/char/int/String/enum(枚举)

循环语句

do
{
判断;
步进语句;
}while(判断语句);

记得结尾加 ‘ ; ’

for的第一个;前不能用2个语句

定义在for里面的变量在循环结束后就消失了

循环控制语句continue,执行立刻跳过当次循环内容,马上开始下一次循环`

死循环
while(true)
{
    
}

可以用ctrl+c结束

在死循环后面写的语句会因为无法使用而编译报错

数组

动态初始化

数组类型[] 数组名称=new 数据类型[数组长度]

int[] arraga=new int[300];
int[] arragc;
arragb = new int[5];
静态初始化

数组类型[] 数组名称=new 数据类型[]{1,2,3,4};

int[] arraga=new int[]{1,2,3,4};//一般定义
int[] arragb={1,2,3,4};//省略定义
int[] arrayc;
arrayc=new int [5]//静态初始化的分步定义
使用

一样使用数组名称【位置】来引用

长度获取

数组名.length

int[] longss = {2,1,34,5,6,7,8,9,9,2};
int len=longss.length;
System.out.println("数组长度"+len);
int[] arrayc=new int[3];
System.out.println("数组长度"+(arrayc.length));//put数组长度3
arrayc=new int[5];
System.out.println("数组长度"+(arrayc.length));//put数组长度5
可变参数

前提:参数列表的数据类型已经确定,参数列表个数不确定,就可以用可变参数

使用:在定义方法时使用

​ 修饰符 返回值类型 方法名 (数据类型…变量名){}

public class Set {
    public static void main(String[] args){
        System.out.println(  add(1,2,3,4,5,6,7));
    }
    public static int add(int...arr)
    {
        int sum=0;
        for (int i : arr) {
            sum+=i;
        }
        return sum;
    }
}

一个方法可变参数只能有一个,如果有其他变量,可变参数要写在最后面

(int a,double b,int…arr)

内存分配

分为5个部分

存放方法中的局部变量

方法的参数在离开方法后立刻从栈中消失

方法的运行一定在栈当中

用new出来的东西,都存放在堆中

地址采用16进制

会给堆里面赋默认值

int 0

float 0.0

char ’\u0000‘

boolean false

String null

方法区

存放.class文件的相关信息

本地方法栈

与操作系统相关

寄存器

与cpu相关

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sKilA386-1627563797812)(D:\Program Files (x86)]\Typora\笔记内容\内存分配.jpg)

类与对象

类是一类事物的描述,是抽象的

对象是一类事物的实例,是具体的

类是对象的模板,对象是类的实体

成员变量是直接定义在类当中的,在方法外面

成员方法不要写static

package Student;

public class student {
    String name;
    int age;
    public void eat()
    {
        System.out.println("eat food");
    }
    public void sleep()
    {
        System.out.println("spend long time sleep");
    }
    public void study()
    {
        System.out.println("spend short time study");
    }
}

使用

1.导包:

import 包名称.类名称;

对于和当前类在同一个包的,可以不用导包

package Student;

import phone.phone;//导包要在package这串代码之下
import Student.student;

public class yingyong {
    public static void main(String[] args) {
        student stu = new student();
        phone stus = new phone();
        stus.call("mom");
        stu.eat();

    }
}

2.创建:

类名称 对象名 = new 类名称();

Student stu = new Student();

也可以

public static void method(Student abc)

{
System.out.println(abc.name);
System.out.println(abc.age);
}

3.使用:

使用成员变量:对象名.成员变量名

使用成员方法:对象名.成员方法名(参数)

如stu.name

//创建:
Student stu = new Student();

//使用
System.out.println(stu.name);
System.out.println(stu.age);

标准类

1.所有成员都要有private

2.每个成员都有Get和Set方法//可以用Alt+Inset键快速生成或是Code里的Generate

3.编写一个无参数的构造方法//上面的一样操作后选第一个,再选中间的Select none0

4.编写一个全参数的构造方法//一样操作后,选ok

Static

一旦使用了static关键字,内容不再属于对象,而是属于类的,凡是对于整个类的,都共享统一内容

(无法通过外部导包的形式对static变量进行赋值)

    private int id;
    private String name;
    private int age;
    static String room;
    private static int idCounter=0;
    public STUDENT() {
        this.id=++idCounter;//通过定义的idCounter的变量来实现对学号的排序统计
    }

    public STUDENT(String name, int age) {
        this.name = name;
        this.age = age;
        this.id=++idCounter;
    }

静态代码块

/* pulic class 类名称{

static{

静态代码块内容;	

}
}*/
public class static_code {
    static{
        System.out.println("静态代码块执行啦");
    }
    private static int counter=0;
    public static_code()
    {
        System.out.println("构造方法开始运行");
        if(++counter!=1)
        {
            System.out.println("静态代码块已经执行过啦,不会再执行啦");
        } 
    }
}

运行

public class static2 {
    public static void main(String[] args) {
        static_code one=new static_code();
        static_code two=new static_code();
    }
}
//静态代码块执行啦
//构造方法开始运行
//构造方法开始运行
//静态代码块已经执行过啦,不会再执行啦

特点是当第一用到本类时,静态代码块执行唯一的一次

主要用于一次性的对静态成员变量进行赋值

内部类

一个类内部包含另一个类

1.成员内部类

定义格式

修饰符 class 类名称{

//…

​ 修饰符 class 内部类名{

​ // …

​ }

}

内用外,随意访问,外用内,需要内部对象

间接调用

public class Innerclass1 {
    public static void main(String[] args) {
        Body body = new Body();
        body.methodBody();
        //通过外部类方法,调用内部类方法
    }
}
public class Body {
    private String name;
    //外部类变量
    public class Heart
    {//内部类
        public void beat()
        {
            System.out.println("我是内部类的");
            System.out.println("我叫:"+name);
        }
    }

    public void methodBody()
    {
        System.out.println("外部类的方法");
        new Heart().beat();
        //匿名对象调用内部方法
    }
    public String getName()
    {
        return name;
    }

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

直接调用

外部类名称.内部名称 对象名 = new 外部类名称() . new 内部类名称();

public class Innerclass1 {
    public static void main(String[] args) {
        Body.Heart heart = new Body().new Heart();
        heart.beat();
        //通过直接定义使用内部方法
    }
}

访问内部类重名变量

public class innerclass2 {
    String str = "外部类变量";
    public class inner{
        String str = "内部类变量";

        public void methodInner(){
            String str="内部类方法变量";
            System.out.println(str);
            System.out.println(this.str);
            System.out.println(innerclass2.this.str);
        }
    }
}
public class use_innerclass {
    public static void main(String[] args) {
        innerclass2.inner out = new innerclass2().new inner();
        out.methodInner();
    }
}
2.局部内部类(包含局部内部类)

如果一个类时定义在一个方法内部的,那就是一个局部内部类

格式

修饰符 class 外部类名{

​ 方法(){

​ class 局部内部类名称{

​ //。。。

​ }

​ }

}

public class body {
    public void methodOuter()
    {
        class heart
        {
            String str = "这是一个局部内部类";
            public void methodheart()
            {
                System.out.println(str);
            }
        }
        new heart().methodheart();
    }
}
public class use {
    public static void main(String[] args) {
        body bodyuse = new body();
        bodyuse.methodOuter();
    }
}

局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是{final的}

匿名内部类

在接口的实现类(或是父类的子类)只需要使用唯一的一次

那就可以省略该类的定义,改为使用【匿名内部类】

格式

接口名 对象名 = new 接口名(){

​ //覆盖重写所有抽象方法

}

public interface MyInterfcae {
    public abstract void method();
	//接口定义
}
public class MyInterface_use {
    public static void main(String[] args) {
        MyInterfcae obj = new MyInterfcae() {
            @Override
            public void method() {
                System.out.println("匿名内部类使用ing");
            }
        };
        obj.method();
        //有对象名的匿名内部类,省略的是实现类(不用再定义一个使用类来使用)
        new MyInterfcae() {
            @Override
            public void method() {
                System.out.println("匿名内部类使用ing");
            }
        }.method();
        //匿名对象的内部类,不过只能使用其中其中一个方法
    }
}

包装类

装箱与拆箱

装箱

把基本类型的数据,包装到包装类当中(基本类型数据->包装类)

//装箱
Integer int1 = new Integer(1);
Integer int2 = new Integer("1");
System.out.println(int1+"<>"+int2);
//1<>1,上面的函数可以接收int和String类型的输入
Integer int3 = Integer.valueOf(2);
Integer int4 = Integer.valueOf("2");
System.out.println(int3+"<>"+int4);
//2<>2上面的方法是属于静态的方法

拆箱

把包装类中的数据,放的基本类型中去

//开箱
int i = int1.intValue();
System.out.println(i);
//将int1中的数取出放到i中

自动装箱拆箱

Integer in =1;
//把int类的数据自动装箱变成Integer
Integer in2 = in +2;
//相当于自动开箱变成int类型再进行运算
System.out.println(in+"\n=======\n"+in2);
基本数据类型与字符串的转化

基本类型–>字符串

1.基本类型的值+“”就可以转化为字符串

2.使用包装类中的静态toString方法 ,返回一个String类型的数据

3.使用String中的vauleOf(int i),返回的是int参数的字符串表现形式

String str1  = 100+"";
System.out.println(str1+200);
//因为经过了字符串的转化,所以打印的是100200
String str2 = Integer.toString(100);
System.out.println(str2+200);
String str3 = String.valueOf(100);
System.out.println(str3+200);

字符串–>基本类型

用包装类中的parseXX(“字符串”)

System.out.println(Integer.parseInt("100")+200);
//300,因为已经转化为了整型,所以打印300

对象

面向对象的特性:封装,继承,多态

封装

1.方法就是一种封装(将具体的细节步骤合并起来)

2.关键字private也是一种封装

在定义类的时候在不允许修改的变量前加上private,该变量便只能在该类中使用

要使用的话,要通过定义的方法使用

private int age;
private boolean male;
public void setMale(boolean b)
{
    male=b;
}
public boolean isMale()//记住boolean要用isXxx命名获取函数
{
    return male;
}
public void setAge(int num)//输入的只能用setXxx命名
{
    age=num;//且不能有返回值
}
public int getAge()//输出的只能用getXxx命名,注意首字母大写
{
    return age;//且一定要有放回值
}

3.this的使用

public class Person{
    String name;//自己的名字
    public void saynihao(String name){
        System.out.println(name+"Hello,i am "+this.name);//这个this.name来调用成员变量name
    }
}

匿名对象

public class shiyongbao {
    public static void main(String[] args) {
        new bao().name="youfather";
    }
}

public class bao {
    String name;
    public void person(){
        System.out.println("我叫"+name);
    }
}

也可以用在一次输入上

package niming;
import java.util.Scanner;

public class shiyongbao {
    public static void main(String[] args) {
        System.out.println("请输入数字");
        int num=new Scanner(System.in).nextInt ();//正常的匿名输入
        System.out.println("输了"+num);
        scanf(new Scanner(System.in));
        Scanner sc = scanf();
        int a=sc.nextInt();
        System.out.println("输入的数字为"+a);
    }
    public static void scanf(Scanner sc)//参数传递的匿名
    {
        int num=sc.nextInt();
        System.out.println("输了"+num);
    }

    public static Scanner scanf()//匿名的返回值
    {
        return new Scanner(System.in);
    }
}

继承

是多态的前提,无继承无多态

主要用于解决共性抽取

调用

package extend;
/*
定义父类和定义普通类没什么区别
public class 父类名
{
。。。;
}
子类public class 子类名 extends 父类名
{
。。。;
}
 */
public class extend1 {
    public static void main(String[] args) {
        teacher man = new teacher();
        man.employ();
    }
}

定义

package extend;

public class employ {
    public void employ()
    {
        System.out.println("方法开始运行");
    }
}
package extend;

public class teacher extends employ{

}

**一些小规则 ** 对于一些重名对象的

public class extends2 {
    public static void main(String[] args) {
        zi formzi=new zi();
        fu formfu=new fu();
        System.out.println(formzi.strZi);//这是一个子类的字符串
        System.out.println(formzi.strFu);//这是一个父类的字符串
        System.out.println(formzi.str);//这是一个父子都有的,但是来自于子的字符串
        System.out.println(formfu.str);//这是一个父子都有的,但是来自于父的字符串
        /*
        如果父子类中有重名的变量,那么就看应用时所定义的类,来自父就是父的
         */
        System.out.println("======================================");
        formzi.methodZi();//这是一个父子都有的,但是来自于子的字符串
        formzi.methodFu();//这是一个父子都有的,但是来自于父的字符串
        formfu.methodFu();//这是一个父子都有的,但是来自于父的字符串
        /*
        这个方法是谁的,就优先调用那个类里面的变量,如果类里面没有,再向上找父亲里有没有
         */
    }
}
public class fu {
    String strFu = "这是一个父类的字符串";
    String str = "这是一个父子都有的,但是来自于父的字符串";
    public void methodFu()
    {
        System.out.println(str);
    }
}
public class zi extends fu{
    String strZi = "这是一个子类的字符串";
    String str = "这是一个父子都有的,但是来自于子的字符串";
    public void methodZi()
    {
        System.out.println(str);
    }
}

关于一些变量的引用,增加了super的使用

public class Zi extends Fu {
    String str = "子";
    public void method()
    {
        String str = "小儿子";
        System.out.println(str);//局部变量
        System.out.println(this.str);//子类的变量
        System.out.println(super.str);//父类的变量
    }
}
public class Fu {
    String str = "父";
}
public class extend3 {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method();
        /*(结果)
        小儿子
        子
        父
         */
    }
}
继承的方法重写

在继承关系中,方法的名称一样,参数列表也一样

创建的是子类对象,优先用子类对象

(可以用@Override来验证是不是规范重写)

(重写子类的返回值必须小于或等于父类)

如父的Object,子可以String/Object作方法的返回值

(子类的权限必须大于或等于父类方法的权限修饰符)

public>protected>(default)>private

其中(default)不是一个关键字,而是什么都不写,留空

public class Fu {
    public Fu(int num)
    {
        System.out.println("这个是一个带参的父类方法");
    }
    public Fu()
    {
        System.out.println("父类构造方法开始");
    }
}
public class Zi extends Fu {
    public Zi()
    {
        super(20);//可以用super来调用父类有参构造,super必须是构造方法里的第一句,不能一个构造方法多次调用super
        //子类如果没有写super();,会默认送一个super()
        System.out.println("子类构造方法开始");
    }
}
public class use {
    public static void main(String[] args) {
        Zi zi = new Zi();//父类构造方法开始
                        //子类构造方法开始,先调用父类,再调用子类
    }
}
super关键字使用

1.在子类成员方法中,访问父类的成员变量

2.在子类成员方法中,访问父类成员方法

3.在子类构造方法中,访问父类的构造方法

this关键字的使用

1.在本类的成员方法中,访问本类的成员变量

2.在本类的成员方法中,访问本类的另一个成员方法

3.在本类的构造方法中,访问本类的另一个构造方法

(如在本类的无参构造中调用本类的有参构造)

public Zi()
{
    this(123);//只能在第一行使用,也必须是唯一一句
    //this(123),不能出现这个
    //super和this不能同时使用,同时使用会冲突
}
public Zi(int num)
{
    
}
继承的小特点

1.一个类的直接父类只能有唯一一个

2.java可以多级继承

可以有爷爷的存在

class a{}->class b extends a{}->class c extends b{}

3.一个父类可以有很多的子类

抽象

抽象方法 就是在方法的返回值前面加上abstract,然后去掉大括号,直到分号结束

抽象类 抽象方法所在的类,必须是抽象类,在class前加上abstract即可

public abstract class Animal {
    public abstract void eat();//不能直接使用父类中的抽象类
    public Animal()
    {
        System.out.println("父类构造方法执行!!");
    }
}

子类使用

//子类必须覆盖重写父类中的所有抽象方法
//覆盖重写去掉abstract并补上大括号
public class fish extends Animal {
    public fish()
    {
        System.out.println("子类构造方法执行!");
    }
    @Override
    public void eat()
    {
        System.out.println("大鱼吃小鱼");
    }
}

接口

接口时一种公共的规范标准,只要符合规范,大家就可以通用

接口不能有静态代码块和方法定义

一个方法可以有多个接口

接口可以有多个父接口继承

(格式:public class 实现方法类名 implements 接口1,接口2,…)

接口定义

public interface 接口名称{

//接口内容;

}

接口内容到java9可以有

1.常量

2.抽象方法(java7以后)

注意抽象方法的定义中,修饰符必须要public abstract开头

不过可以选择性省略public/abstract或是全部省略(不推荐)

对于重复的抽象方法,只需要覆盖重写一次即可

3.默认方法 用来解决接口升级问题

public default 返回值类型 方法名称(参数列表)

{

方法体;

}

如果在多接口中有重复的默认方法,要对重复的默认方法进行覆盖重写

如果有继承的父类和默认方法有重复的,优先执行父类的默认方法

4.静态方法(java8以后)

和默认方法差不多

就是把default换成static

public interface staticinterface {
    public static void methodStatic()
    {
        System.out.println("这是一个静态方法");
    }
}
public class usestaticinterface {
    public static void main(String[] args) {
        staticinterfaceImpl impl = new staticinterfaceImpl();
        //impl.methodStaic;错误写法,无法这样调用
        //要调用接口的静态方法,直接用接口名称.方法名即可
        staticinterface.methodStatic();
    }
}

5.私有方法(java9以后)

普通私有方法,解决多个默认方法之间重复代码问题

private 返回值类型 方法名(参数表){ 方法体; }

静态私有方法

private static 返回值类型 方法名 (参数列表){方法体;}

记得在开始创建java文件的时候选Interface,不要选class

接口使用

1.要定义一个实现类来使用

public class 实现类名称(最好是接口名称加上‘Impl’) implements 接口名称

{

//…

}

package Interface;

public interface Interface1 {
    void method1();
    public abstract void method2();
}

2.接口的实现类必须覆盖重写接口中所有的抽象方法

实现:去abstract关键字,加上方法体大括号

public class Interface1Impl implements Interface1 {
    @Override
    public void method1() {
        System.out.println("这是第一个方法");
    }

    @Override
    public void method2() {
        System.out.println("这是第二个方法");
    }
}

3.创建实现类对象,进行使用

public class useinterface1 {
    public static void main(String[] args) {
        //Interface1 inter = new Interface1() ;错误写法
        Interface1Impl impl = new Interface1Impl();
        impl.method1();
        impl.method2();
    }
}

接口的成员变量

public class useInterface_Const {
    public static void main(String[] args) {
        System.out.println(Interface_Const.NUM);
        //接口常量的使用
    }
}
public interface Interface_Const {
    public static final int NUM=10;//定义的常量,一旦赋值,不可修改
    //1.用final关键字进行修饰,说明不可改变
    //前面的public static final 其实都可以省略的
    //2.必须附初值,且名称必须大写
}

多态

代码中多态的体现:父类引用指向子类对象

格式:

父类名称 对象名 = new 子类名称();

​ 接口名称 对象名 = new 实现对象类名();

可以

对象名.父类方法/变量来引用父类中的变量

​ 对于方法的话,优先级是看new右边的对象

​ 对于变量,优先看new左边的对象

引用

public class useMulti {
    public static void main(String[] args) {
        Fu dogson = new Zi();
        dogson.method();//这是子类的方法
        System.out.println(dogson.num);//20
    }
}
public class Fu {
    public void method()
    {
        System.out.println("这是父类的方法");
    }
    int num=20;
}
public class Zi extends Fu{
    public void method()
    {
        System.out.println("这是子类的方法");
    }
    int num = 10;
}

编译看定义的左边是父还是子(从左边开始找),运行看定义的右边是父还是子

(运行从右边开始找)对应成员方法,成员变量都看左边

对象的向上转型

格式就是多态写法,就是创建一个子类对象,把它当做他爸爸对待

但是,这样的方法使用上会让子类特有的方法无法使用

对象的向下转型

格式:子类名 对象名 = (子类名)父类对象;

将父类对象还原为子类对象

必须保证对象原本的类型是要向下转型的对象才能向下转型

(如把猫当动物,还原就是把动物还原回猫,但不能还原为狗)

public abstract class Animal {
    public abstract void eat();
}
public class cat extends Animal {
    @Override
    public void eat() {
        System.out.println("小猫爱吃鱼");
    }

    public void walk()
    {
        System.out.println("小猫走猫步");
    }
}
public class dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃Shit");
        //不要问这是吃的啥子
    }
}
public class use {
    public static void main(String[] args) {
        Animal animal = new cat();
        animal.eat();//小猫爱吃鱼
        cat Cat = (cat) animal;
        //将cat(动物)还原为猫
        Cat.walk();
        //小猫爱吃鱼
        //dog dog1 = (dog) Cat;
        // 这个编译没问题,但运行会报错
        if(animal instanceof cat)
        {
            cat Cat2 = (cat) animal;
            Cat2.walk();
        }
        if(animal instanceof dog)//instanceof 用于判断对象原本的类型,对象名写前面,类名称写后面 ,是的话返回true
        {
            System.out.println("animal是一个dog类");
        }
    }
} 
final的使用

修饰类时

public final class 类名称{

//。。。

}

这个类不能有任何子类(太监类)

因为final无子类,所以里面的方法无法被覆盖重写

如果用final定义方法,那么改方法就无法被覆盖重写

如果用final定义变量,那么该变量只能被赋值一次

如果用final定义引用类型,那么该变量的地址无法改变(无法new),但是可以用setXxx从新赋值

IDEA使用

create–>Empty Project–>Modules(+)New Modules -->左边的src文件–>new package–>在package中new一个java

可以在代码中输入psvm加回车生成开始代码

输出代码打入sout就可以

自动for循环–>循环次数.fori

运行:点击右键 --> run即可,结果显示在下方

IDEA常用快捷键

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3seapl5-1627563797814)(D:\Program Files (x86)]\Typora\笔记内容\SharedScreenshot.jpg)

API

java提供给我们的现成的类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XARH4f3O-1627563797815)(D:\Program Files (x86)]\Typora\笔记内容\类存在的包.jpg)

上面蓝色部分为类所存放的包

使用上要先看构造方法摘要

java.lang下的包不需要导包

Scanner类

实现从键盘中的输入

import java.util.Scanner;

public class scan {
    public static void main(String[] args) {
        int a,b;
        Scanner sc= new Scanner(System.in);//后面的System.in是接收从键盘的输入
        System.out.print("请输入求和的第一个数字");
        a=sc.nextInt();
        System.out.print("请输入求和的第二个数字");
        b=sc.nextInt();
        System.out.println("两个数的和为"+sum(a,b));
    }
    public static int sum(int a,int b)
    {
        return a+b;
    }
}

Random类

生成随机数字

import java.util.Random;

public class ran
{
    public static void main(String[] args) {
        Random ri=new Random();
        System.out.println("随机数生成了"+ri.nextInt());//范围巨大的类型
        System.out.println("随机生成了一个范围内的数字"+ri.nextInt(10));//范围是[0,10);
    }
}

一个猜数字的小游戏

import java.util.Random;
import java.util.Scanner;

public class rangame {
    public static void main(String[] args) {
        Random rn=new Random();
        int end=rn.nextInt(100)+1;
        System.out.println("游戏开始");
        System.out.println("游戏自动生成1~100之间的整数");
        System.out.println("请通过输入数字不断靠近目标数字,步数越少得分越高");
        Scanner sn = new Scanner(System.in);
        int num=sn.nextInt();
        int i=1;
        while(num!=end)
        {
            if(num>end)
            {
                System.out.println("输入数字大于目标值,当前第"+(i++)+"步");
            }
            else if(num<end)
            {
                System.out.println("输入数字小于目标值,当前第"+(i++)+"步");
            }
            num=sn.nextInt();
        }
        System.out.println("恭喜你获得胜利,当前步数"+i);
    }
}

ArrayList

对于ArrayList ,有一对<>代表泛型(就是装在集合中的所有元素,全都是统一的什么类型,ps泛型只能是引用类型 ,不能是基本类型)

import java.util.ArrayList;

public class clsss {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        System.out.println(list);//直接打印内容为空,得到的是[]
        System.out.println("=================");
        list.add("wo ai ni");
        System.out.println(list);//[wo ai ni]
        System.out.println("=================");
        list.add("i love china");
        list.add("china long age");
        System.out.println(list);//[wo ai ni, i love china, china long age]
    }
}

几个基本命令

import java.util.ArrayList;

public class arraylist_usual_ff {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("林泽霖");
        list.add("林颖超");
        System.out.println("添加林政佳这一动作是否成功"+ list.add("林政佳"));
        list.add("林子程");//添加林政佳这一动作是否成功true
        System.out.println(list);//[林泽霖, 林颖超, 林政佳, 林子程]

        String name=list.get(1);//第1位是林颖超
        System.out.println("第1位是"+name);

        String removename = list.remove(3);
        System.out.println("被删除的是"+removename);//被删除的是林子程
        System.out.println(list);

        int size=list.size();
        System.out.println("链表长度为"+size);//链表长度为3
        
        for (int i = 0; i < list.size(); i++) {
        System.out.println("第"+i+"位"+"是"+list.get(i));
        }//数组遍历
    }
}

因为ArrayList无法存放基本类型,所以需要使用一些被封装好的基本类型

int–Integer

char–Character

其他都是将首字母大写

String类

因为字符串的内容永不改变,所以可以共享使用

使用

1.public String();

2.public String(char[] array);根据字符数组来创建

3.public String(byte[] array);根据字节数组来创建

public static void main(String[] args) {
         String str1 = new String();
        System.out.println("普通输出的是"+str1);
        char[] cs = {'1','2','3'};
        String str2 = new String(cs);
        System.out.println("字符输出的是"+str2);
        byte[] bs = {48,49,50};
        String str3 = new String(bs);
        System.out.println("字节输出的是"+str3);
    }

字符串常量池(在堆当中)

程序中直接写的"",就在常量池中

对于基本类型的比较,是对数值的比较

而对于引用类型,是对地址的比较

有equals可以对字符串进行比较

一样返回ture,不一样放回false

public class String2 {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        char[] array = {'a','b','c'};
        String str3=new String(array);

        System.out.println(str1.equals(str2));
        System.out.println(str2.equals(str3));
        System.out.println(str3.equals(str1));
        System.out.println("===================");
        String str4="ABC";
        System.out.println(str1.equals(str4));
        System.out.println(str1.equalsIgnoreCase(str4));//忽略大小写
    }

还有一些关于字符串的拼接和索引函数

public class String3 {
    public static void main(String[] args) {
        String str = "I love ";
        System.out.println("字符串长度为"+str.length());//获取字符串长度
        String str2 = "China ";
        String str3 = str.concat(str2);
        System.out.println(str);//拼接后原字符保持不变
        System.out.println(str2);
        System.out.println(str3);
        char ch = str3.charAt(0);
        System.out.println(ch);//获取字符串索引位置的字符
        int num1 = str3.indexOf("ina");
        int num2 = str3.indexOf("ax");
        System.out.println("ina位于"+num1);//返回第一次出现的索引位置
        System.out.println("ax位于"+num2);//无的返回值为-1
    }

记得字符串的改变是对于字符串的字符串所代表的变量有的地址的改变

public class String4 {
    public static void main(String[] args) {
        String str1="I love China";
        String str2=str1.substring(2);
        String str3=str1.substring(2,6);//截取[2,6)范围内的字符串 
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
    }
}

字符串的替换和转化

public class String5 {
    public static void main(String[] args) {
        char[] chars = "I love China".toCharArray();//将字符串转化为字符数组
        for (int i = 0; i < chars.length; i++) {
            System.out.print(chars[i]);
        }

        byte[] bytes = "I love China".getBytes();//将字符串转化为字节数组
        for (int i = 0; i < bytes.length; i++) {
            System.out.println(bytes[i]);
        }

        String str1 = "I love CHINA";
        String str2 = str1.replace("HINA","hina");//将str1里的HINA替换为hina
        System.out.println(str1);
        System.out.println(str2);
    }

字符串分割

public class String6 {
    public static void main(String[] args) {
        String str1 = "I love China";
        String[] str2 = str1.split(" ");//按空格切割
        for (int i = 0; i < str2.length; i++) {
            System.out.println(str2[i]);
        }
        String str3 = "L.L.L";
        String[] str4 = str3.split(".");//单写 . 会切失败
        for (int i = 0; i < str4.length; i++) {
            System.out.println(str4[i]);//没有显示
        }
        String[] str5 = str3.split("\\.");
        for (int i = 0; i < str5.length; i++) {
            System.out.println(str5[i]);//要用\\.才能切成功
        }
    }

Arrays类

内含大量静态方法,实现数组相关的大量操作

import java.util.Arrays;

public class static3 {
    public static void main(String[] args) {
        int[] arrayA={1,2,3,4};
        int[] arrayB={19,45,26,34,2};
        String int_str = Arrays.toString(arrayA);//将数组按默认格式转化为字符串
        System.out.println(int_str);//[1, 2, 3, 4]

        Arrays.sort(arrayB);//对数组进行排序
        System.out.println(Arrays.toString(arrayB));//[2, 19, 26, 34, 45]
    }
}

Math类

public class MATH1 {
    public static void main(String[] args) {
        System.out.println(Math.abs(-3.14));//取绝对值3.14
        System.out.println(Math.ceil(4.1));//向上取整5
        System.out.println(Math.floor(3.61));//向下(取整)抹零3
        System.out.println(Math.round(3.4));//四舍五入
       	System.out.println(Math.PI);
    }
}

Object类

最根本的父类,描述的所有方法子类都可以使用

toSring()

返回该对象的字符串表示

直接打印对象的名字,就是调用toString方法来显示对象的地址

public class Object_study1 {
    public static void main(String[] args) {
        Person p = new Person("王多鱼",18);
        String s = p.toString();
        System.out.println(s);
        //Object_study.Person@75412c2f
        System.out.println(p);
        //Object_study.Person@75412c2f
        //直接打印对象的名字,就是调用toString方法,使p=p.toString
    }
}
public class Person {
    public String toString() {
    //原来的返回值return super.toString();
    return "name="+name+",age = "+age+"";//现在重写完toString方法后就可以显示变量名了
	}
}

equals()

比较的是两个对象的地址值

内部是

public boolean equals(Object obj)
{
    return (this==obj);
    //this是对象
    //obj是对象传递过来的参数
}
public class Objec_study2 {
    public static void main(String[] args) {
        Person a= new Person("王多鱼",18);
        Person b= new Person("刘二狗",34);
        Person c = new Person("王多鱼",18);
        System.out.println(a.equals(b));
        //false
        System.out.println(a.equals(c));
        //true
    }
}
//重写equals,依旧是在Person类下重写的
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Person) {
            //先向下转型,将obj转为Person
            Person p = (Person) obj;
            return (this.name.equals(p.name) && this.age == p.age);
            //前面的是比较name的字符串,后面是比较age的值
            //return super.equals(obj);
        }
        else {
            return false;
        }
    }

Date类

import java.util.Date;

public class Date_study {
    public static void main(String[] args) {
        demo1();
    }
    private static void demo1()
    {
        Date date = new Date();
        System.out.println(date);
        //打印现在的时间
        Date date2= new Date(0L);
        System.out.println(date2);
        //打印0毫秒时间(中国的)
        System.out.println(date.getTime());
        //获取当前时间的毫秒值
    }
}

DateFormat类

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

public class DateFormat_study {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒");
        String text=sdf.format(new Date());
        //返回值是String
        System.out.println(new Date());
        //按照指定格式打印
        System.out.println(text);
        Date text2 = sdf.parse("2021年03月25日:23时09分37秒");
        //把符合模式的字符串回归到初始格式
        //因为方法定义的问题,所以会有异常显示,Alt+回车处理下就可以
        System.out.println(text2);
    }
}

System类

System.currentTimeMillis();获取当前毫秒值

public class System1 {
    public static void main(String[] args) {
        long s=System.currentTimeMillis();
        //获取当前时间的毫秒值
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
        long ns=System.currentTimeMillis();
        //获取结束后的毫秒
        System.out.println("程序运行了"+(ns-s)+"毫秒");
    }
}

System.arraycopy

import java.lang.reflect.Array;
import java.util.Arrays;

public class System1 {
    public static void main(String[] args) {
        int[] src ={1,2,3,4,5};
        int[] dest={6,7,8,9,10};
        System.out.println("复制前"+ Arrays.toString(dest));
        System.arraycopy(src,0,dest,0,3);
        //将src中的第一位开始复制到dest的第一位共复制3位
        System.out.println("复制后"+ Arrays.toString(dest));
    }
}

StringBuilder类

public class StringBuilder_study {
    public static void main(String[] args) {
        StringBuilder bu1 = new StringBuilder();
        System.out.println(bu1);
        //打印为空,因为没有给bu1中赋予字符串
        System.out.println("========================");
        StringBuilder bu2 = new StringBuilder("abc");
        System.out.println(bu2);
        //打印abc
        System.out.println("========================"  );
        StringBuilder bu3 = new StringBuilder();
        StringBuilder bu4 = bu3.append("我爱你");
        //在bu3中添加字符串并返回到原来的字符串中
        System.out.println(bu3+"<>"+bu4);
        System.out.println(bu3==bu4);
        //返回为true说明地址是一样的
        //所以使用append方法无需设置返回值
        System.out.println("========================"  );
        bu4.reverse();
        System.out.println(bu4);
        //反转字符串中的内容
    }
}

String与StringBuilder的相互转换

StringBuilder可以用默认的构造方法转化为String类

String可以使用StringBuilder方法中的toString方法来转化为String

String s= "I love China";
StringBuilder sb = new StringBuilder(s);
//将s转化为StringBuilder类型并赋值到sb中
System.out.println(s+'\n'+sb);
System.out.println("============");
String b = sb.toString();
//将sb中的值转化为字符串赋值到b中
System.out.println(b);

File类

主要用于操作文件和文件夹

String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);
//打印路径分隔符   ;
String separator = File.separator;
System.out.println(separator);
//打印文件名称分隔符   \
//不同系统的文件符号不一样的的

路径

绝对路径:一个完整的路径,以盘符开始的路径(d:,c:)

相对路径:一个简化的路径

​ 如果是在当前项目的根路径下操作,可以直接使用文件的名称(如: 123.txt)

一些关于路径名的方法

File f1 = new File("a.txt");
//传递文件路径的字符串
System.out.println(f1);
//a.txt
File f2 = new File("C:\\","a.txt");
//C:\a.txt
//前面的是父路径,后面的是子路径
System.out.println(f2);
File parent = new File("c:\\");
File f3 = new File(parent,"c.txt");
System.out.println(f3);
//c:\c.txt

File类获取功能的方法

File f1 = new File("C:\\Users\\啦啦啦\\Desktop\\a.txt");
System.out.println(f1.getAbsolutePath());
//获取文件的绝对路径,不管你放的是绝对还是相对,返回的都是绝对的路径的字符串
System.out.println(f1.getPath());
//类型中存放的是什么路径返回就是什么路径
System.out.println(f1.getName());
//获取路径文件的名
System.out.println(new File("C:\\Users\\啦啦啦\\Desktop\\神奇的小本本.txt").length());
//637-->存在的就返回字节数
System.out.println(f1.length());
//不存在的就返回0
//注意文件夹大小也是返回0

File中的一些判断功能

File f1 = new File("C:\\Users\\啦啦啦\\Desktop\\神奇的小本本.txt");
System.out.println(f1.exists());
//返回true说明构造方法中的路径存在,false说明构造方法中的路径不存在
System.out.println(f1.isFile());
//判断是否是文件
System.out.println(f1.isDirectory());
//判断是否是文件夹

创建和删除功能

File f1 = new File("C:\\Users\\啦啦啦\\Desktop\\abc.txt");
File f2 = new File("C:\\\\Users\\\\啦啦啦\\\\Desktop\\\\神奇的小本本.txt");
File f3 = new File("C:\\Users\\啦啦啦\\Desktop\\abc");
File f4 = new File("C:\\Users\\啦啦啦\\Desktop\\abc\\BCD");
System.out.println("创建文件abc.txt  "+f1.createNewFile());
System.out.println("创建文件神奇的小本本.txt "+f2.createNewFile());
//创建成功返回true,失败返回false
//相对路径也可以,但是吧有点懒得搞,理解就好
System.out.println("删除文件abc.txt  "+f1.delete());
//删除文件abc.txt成功,如果文件夹里有东西,不会删除,返回false
System.out.println("创建文件夹abc "+f3.mkdir());
//创建文件夹abc true
System.out.println("创建文件夹BCD "+f4.mkdir());
//创建文件夹BCD true-->这个是创键的一个多级文件夹

File遍历目录

File f1 = new File("C:\\Users\\啦啦啦\\Desktop");
//将目录下的所有文件,放到arr数组中
String[] arr = f1.list();
for (String s : arr) {
    System.out.println(s);
}
File[] file  = f1.listFiles();
//获取f1目录下的所有文件的目录,并保存到File中
for (File file1 : file) {
    System.out.println(file1);
}

文件过滤器

import java.io.File;
import java.io.FileFilter;

public class FileFilterImpl implements FileFilter {
    @Override
    public boolean accept(File pathname) {
        if(pathname.isDirectory())
        {
            return true;
            //判断是否为文件夹,是也返回true
        }
        return pathname.getName().toLowerCase().endsWith(".pdf");
        //在accept方法中定义筛选规则
    }
}
//使用
File[] files = file.listFiles(new FileFilterImpl());
//使用过过滤器的listFiles方法只会将筛选出来的文件列入files中

集合(Collection)

//一些集合可以用的方法
Collection<String> coll = new ArrayList<>();
System.out.println(coll.add("张三"));
System.out.println(coll);
//打印true,表示添加成功
coll.add("我爱罗");
coll.add("辣鸡");
System.out.println(coll.remove("辣鸡"));
System.out.println(coll);
//打印true,表示删除辣鸡成功
System.out.println(coll.contains("我爱罗"));
//打印true,说明里面包含我爱罗这个元素
System.out.println(coll.isEmpty());
//打印false,说明该数组非空
System.out.println(coll.size());
//打印集合元素个数
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}//将集合转化为数组
coll.clear();
System.out.println(coll);
//清空集合
import java.util.ArrayList;
import java.util.Collections;

public class Collection3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"a","b","c");
        //可以一次性往集合中添加大量元素
        System.out.println(list);
        Collections.shuffle(list);
        //打乱集合中的元素
        System.out.println(list);
        //[c, b, a]

    }
}
import java.util.ArrayList;
import java.util.Collections;

public class Collection3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"a","b","c");
        //可以一次性往集合中添加大量元素
        System.out.println(list);
        Collections.shuffle(list);
        //打乱集合中的元素
        System.out.println(list);
        //[c, b, a]
        Collections.sort(list);
        //将集合按默认排序,但因为自定义类没有默认排序的方法,所以自定义类无法使用sort方法
        System.out.println(list);
        //需要重写的排序函数
        /*public int comparaeTo(类名称 o)
        {
        return    o.getXxx() - this.getXxx();
        }
         */
    }
}

of方法

1.使用上只使用与list接口,map接口和set接口

2.of的返回值是一个不能改版的集合,集合不能再使用add,put添加元素

3.set和map接口在使用时不能出现重复元素,不然会出现异常

List<String> list = List.of("a","b","c","a","d");
Set<String> set = Set.of("a","b","c","a","d");
//出现重复元素会报错
Map<String,Integer> map =Map.of("a",1,"b",2,"c",3,"a",4,"d",5);
//出现重复元素会报错

List接口(Vector,ArrayList,LinkedList)

1.有序的集合(存放和取出元素顺序相同)

2.可以存放重复的数据

3.有索引,可以使用普通的for循环遍历

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("a");list.add("b");list.add("c");list.add("d");
    list.add("a");
    System.out.println(list);
    list.add(3,"我在c与d中间");
    //按指定位置添加元素
    System.out.println(list);
    //[a, b, c, 我在c与d中间, d, a]
    String str = list.set(5,"A");
    //替换索引位置的元素,并返回替换前的元素
    System.out.println(str+"\n"+list);
    //[a, b, c, 我在c与d中间, d, A]
}

LinkedList

底层是一个链表结构,包含大量操作首尾元素的方法。

注意为了使用LinkedList中特有的方法,不能用多态来定义

import java.util.LinkedList;

public class LinkList1 {
    public static void main(String[] args) {
        LinkedList<String> linked=new LinkedList<>();
        linked.add("a");
        linked.add("b");
        linked.add("c");
        //也可以用 linked.addlist("c");

        linked.addFirst("ww");
        //也可以  linked.push("ww");
        //往集合的开头加入ww
        System.out.println(linked);
        //[ww, a, b, c]

        System.out.println(linked.getFirst());
        //获取第一个元素
        System.out.println(linked.getLast());
        //获取集合中的最后一个元素
        System.out.println(linked.removeFirst());
        //移除并返回列表的第一个元素   ww
        System.out.println(linked.removeLast());
        //移除并返回列表的最后一个元素  c
        System.out.println(linked.pop());
        //从列表的栈堆弹出一个元素,类似removeFirst  a
        System.out.println(linked);
        //[b]
    }
}

Vector

可以实现可变长度的数组

单线程集合

Set接口(TreeSet,HashSet(LinkedHashSet))

1.不允许储存重复元素

2.无索引,不能用for循环遍历

3.无序集合(除了LinkedHashSet)

HashSet

是一个无序集合,存储元素和取出元素的顺序可能不一致

底层是一个哈希表结构(查询速度快)

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSet1 {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        Iterator<Integer> it = set.iterator();
        while(it.hasNext())
        {
            int n = it.next();
            System.out.print(n+"     ");
            //1     2     3     输出123表示不允许重复,且无序
        }
    }
}
哈希值

一10进制整数,由系统随机给出 (可以用Object中的一个方法来获取哈希值)

Hash  hash = new Hash();
//这个Hash是自己定义的一个新类,里面是空的
int hashnum  = hash.hashCode();
System.out.println(hashnum);
//返回随机整数,hashCode是保存在Object中的,所以可以直接使用
String str1 = "abc";
String str2 = "abc";
System.out.println(str2.hashCode());
System.out.println(str1.hashCode());
//输出一样的地址

Set集合在使用时会自动为数据分配哈希值,然后在添加元素时,会对比元素的哈希值,如果出现重复,就对比元素的实际值,如果也重复不会添加该重复元素

对于自定义的类

如果没有重写HashSet的相关函数,会将相同的元素加到集合中

重写用Alt加Insert,重写HashCode方法,就可以对比出相同的元素了

LinkedHashSet集合

底层是一个哈希表+链表

是一个有序的(HashSet无序),不允许重复的集合

Iterator接口(迭代器)

要创建一个接口实现类来使用

Collection<String> coll = new ArrayList<>();
coll.add("李大狗");
coll.add("赵二狗");
coll.add("王三狗");
coll.add("刘四狗");
Iterator<String> it = coll.iterator();
//多态
boolean b = it.hasNext();
System.out.println(b);
//判断集合中是否有元素
String s =  it. next();
System.out.println(s);
//取出集合中的元素,并自动往下移
while(it.hasNext())
{
    s=it.next();
    System.out.println(s);
}

增强for循环

int[] arr = {1,2,3,4,5,6,7};
for(int i :arr)
{
    System.out.println(i);
}

本质上是迭代器

注意中间是 :,不是;

ArrayList<String> list = new ArrayList<>();
list.add("abc");list.add("abc");list.add("abc");list.add("abc");
for(String sb:list)
{
    System.out.println(sb);
}//也可以用来遍历集合

MAP集合

1.MAP集合是一个双列集合,一个元素包含2个值(key和value)

2.MAP中的元素key和value的数据类型可以相同,也可以不同

3.MAP集合中的元素,key是不许重复的,value可以重复

4.MAP集合中key和value是一一对应的

Map<String,String> map = new HashMap<>();
String str = map.put("李晨","范冰冰");
System.out.println(str);
//存放的K值对的时,key不重复,返回的value是null
System.out.println(map);
//{李晨=范冰冰}
String str2 = map.put("李晨","范水水");
System.out.println(str2);
//存放K值对是,key重复,返回的Value是被替换的值
System.out.println(map);
//{李晨=范水水}
System.out.println(map.put("王狗蛋","范水水"));
System.out.println(map);
//{李晨=范水水, 王狗蛋=范水水}
String str3 = map.remove("李晨");
//删除key值及其所带的value值,返回被删除的value值
System.out.println(str3);
String str4 = map.remove("李鬼");
//没有返回null
System.out.println(str4);
String str5 = map.get("王狗蛋");
System.out.println(str5);
//获取key对应的value值
System.out.println(map.containsKey("王狗蛋"));
//返回true表示有这个key

Map集合的遍历

1:用键key来找值

Map<String,Integer> map = new HashMap<>();
map.put("a",11);
map.put("b",12);
map.put("c",13);
Set<String>  set  = map.keySet();
//1.用Set获取Map中的每一个key
Iterator<String> it = set.iterator();
//2.用迭代器遍历set集合
while(it.hasNext())
{
    //用map里的get方法获取value
    String key =it.next();
    System.out.println(key+"="+map.get(key));
}

2.用Entry来遍历MAP

Map.Entry<K,V>

作用:用来记录map中键与值的关系(想想结婚证),这个结婚证就是Entry对象

可以用

Set<Map.Entry<K,V>>entrySet(),把集合中内部的多个Entry对象获取出来放到一个Set集合中,对Entry对象中的方法,可以分别用getKey()和getValue()获取key和value

Map<String,String> map = new HashMap<>();
map.put("李晨","范冰冰");
map.put("李鬼","范水水");
map.put("王狗蛋","范水水");
Set<Map.Entry<String,String>> set = map.entrySet();
//用Map中的方法,将map中的entry对象存放到set集合中
Iterator<Map.Entry<String,String>> it = set .iterator();
//使用迭代器获取并遍历Set集合
while(it.hasNext())
{
    Map.Entry<String,String> entry = it.next();
    //用Entry对象来获取key和value值
    System.out.println("key:"+entry.getKey()+"value:"+entry.getValue());
}

HashMap集合

1.底层是hash表,查询速度特别快

2.是个无序的集合

LinkedHashMap集合

1.底层是hash加链表

2.有序集合

Hashtable<K,V>

底层一样是个哈希表,但是不能储存null,是一个单线程的,速度比较慢的表

也是使用Map接口的

虽然被取代了。。。

要记住的是Properties集合,使用上和HashMap差不多

泛型

一种未知的数据类型

E e:Element元素

T t:Type类型

好处:

避免类型转化的麻烦

把运行会报的异常,提升到了编译

坏处:

泛型是什么类型,就只能使用什么类型

有关泛型类的定义与使用

public class Generic<E> {
    private E name;	

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
}
public class Generic_use {
    public static void main(String[] args) {
        Generic<String> name = new Generic<String>();
        name.setName("王二狗");
        System.out.println(name.getName());
        Generic obj = new Generic();
        //不写默认为Object类型
        obj.setName(123);
        System.out.println(obj.getName());
    }
}

有关泛型方法的定义

import java.sql.SQLOutput;

public class Generic<E> {
    public <M> void method1(M m)
    {
        System.out.println(m);
    }
    public static <S> void method2(S s)
    {
        System.out.println(s);
    }
}
public class Generic2 {
    public static void main(String[] args) {
        Generic<String> gen  = new Generic<String>();
        gen.method1("林颖超的叽叽");
        gen.method2("林颖超的吉吉");
        //静态还是不建议创建变量使用
        Generic.method2("静态");
    }
}

关于泛型接口的定义

public class Generic_use {
    public static void main(String[] args) {
        Interface_genericImpl impl = new Interface_genericImpl();
        impl.method("林颖超的唧唧");
        Interface_genericImpl2<String> impl2 = new Interface_genericImpl2<String>();
        impl2.method("林颖超的基基");
    }
public interface Interface_generic<I> {
    public abstract void method(I i);
}
public class Interface_genericImpl implements Interface_generic<String>{
    @Override
    public void method(String s) {
        System.out.println(s);
    }
}
public class Interface_genericImpl2<I> implements Interface_generic <I>{
    @Override
    public void method(I i) {
        System.out.println(i);
    }
}

泛型的统配符

不能创建对象使用,只能作为方法的参数使用

public static void print(ArrayList<?> list)
{
    Iterator<?> it = list.iterator();
    while(it.hasNext())
    {
        Object o = it.next();
        System.out.println(o);
    }
}
ArrayList<Integer> a = new ArrayList<>();
a.add(1);a.add(2);a.add(3);
print(a);

泛型的上限限定

? extends E 此时的泛型必须是本类或是子类

泛型的下限限定

? super E 此时的泛型必须是本类或是父类

数据异常

程序在执行过程中出现的,出现的不正常的状况

异常不是语法错误

一般的异常在方法中无法解决会不断上升到方法的前体,如果到main方法都无法解决,就会上升到JVM去,JVM会将异常打印出来并终止程序

Throwable

java中所有的错误和异常的父类

Exception(编译期异常)

写代码时出现的异常

RuntimeExecption(运行期异常)

运行时出现的小问题

Error(错误)

使得程序无法运行的错误

Debug调试程序

在idea左边双击添加一个小红点(断点)

右键在平时运行的地方下面看到一个Debug运行 点击

f8:逐行执行程序

f7:进入到方法中

shift+f8:跳出方法

f9:跳到下一个断点,没有下一个则结束程序

ctrl+f2:跳出debug模式,停止程序

下方的Console :切换到控制台

异常处理

五大关键字

try,catch,finally,throw,throws

throw关键字

格式

throw new xxxException(“异常产生的原因”);

注意

1.throw关键字必须写在方法内部

2.后面new的对象必须是Exception或是Exception子类对象

3.对于抛出的异常对象,我们必须处理这个异常对象

后面创建的如果是RuntimeException或是其子类异常,可以给JVM处理

如果是编译异常,我们就必须处理这个异常,要么throw,要么try…catch

public static void main(String[] args) {
       int[] arr={1,2,3};
       //int e = getElement(arr,3);
       //出现异常,因为数组没第3号元素
       /*Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at Exception.Exception.getElement(Exception.java:11)
at Exception.Exception.main(Exception.java:6)
        */
       //getElement(null,3);
       //Exception in thread "main" java.lang.NullPointerException: 传递数组的值为空
       getElement(arr,3);
       //Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 索引的位置超出数组范围
       
   }
   public static int getElement(int[] arr,int index)
   {
       if(arr==null)
       {
           throw new NullPointerException("传递数组的值为空");
       }
       if(index<0||index>arr.length-1)
       {
           throw new ArrayIndexOutOfBoundsException("索引的位置超出数组范围");
       }
       int ele = arr[index];
       return ele;
   }

throws关键字

使用throws是把异常对象抛出给方法的调用者处理(自己不处理,给别人处理),最终是给JVM处理,—》中断处理

格式

修饰符 返回值类型 方法名(参数列表) throws AAAException,BBBException…

{

​ throw new AAAException(“产生原因”);

​ throw new BBBException(“产生原因”);

}

注意

throws关键字必须写在声明处

throws关键字后面声明的异常必须是Exception或是其的子类

方法内部如果抛出了多个异常对象,那么throws后面也必须声明多个异常,如果抛出的异常对象有子父类关系,我们就必须处理声明的异常

调用了一个声明抛出异常的方法,我们就必须处理声明的异常

要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM

要么try。。。catch自己处理异常

public class Exception2 {
    public static void main(String[] args) throws FileNotFoundException ,IOException{
        readFile("c:\\a.txt");
        //路径无问题,读取文件
        readFile("c:\\a.jpg");
        readFile("d:\\a.txt");
        //传递的文件不是c:\a.txt
    }
    public static void readFile (String filename)throws FileNotFoundException,IOException
            //注意是备注在参数列表后面
    {
        if(!filename.equals("c:\\a.txt"))
        {
            throw new FileNotFoundException("传递的文件不是c:\\a.txt");
        }
        if(!filename.endsWith(".txt"))
        {
            throw new IOException("文件后缀不对");
        }//这一行在之前的if之后,所以执行不到,但可以当个例子学习
        System.out.println("路径无问题,读取文件");
    }

try…catch

方法就是捕获异常

try{

​ 可能产生异常的代码

}catch{

​ (定义一个异常的变量,用来接收try中抛出的异常对象)

​ 异常的处理逻辑,接收处理的异常对象后,怎么处理对象,

一般在工作中,会把异常的信息记录到一个日志中

}

注意

1.try中可能会抛出多个异常对象,那么就可以使用多个catch处理这些对象

2.如果try中产生了异常,就会立刻执行catch中的异常处理逻辑,执行完毕catch中的逻辑后,继续执行try。。。catch后面的代码

public static void main(String[] args) {
    try{
        readFile("d:\\a.txt");
        readFile("d:\\a.jpg"); 
    }catch(IOException e)
    {//try中抛出什么异常对象,catch就定义什么异常变量,用来接收这个异常对象
        System.out.println("catch-传递的文件后缀不是.txt");
    }
    System.out.println("继续执行的代码");
    /*路径无问题,读取文件
	catch-传递的文件后缀不是.txt//因为出现问题直接进入catch,就没执行if中的语句
	继续执行的代码*/
}
public static void readFile (String filename)throws  IOException
//注意是备注在参数列表后面
{
    if(!filename.endsWith(".txt"))
    {
        throw new IOException("文件后缀不对");
    }
    System.out.println("路径无问题,读取文件");
}

Throwable类

主要有3个小方法用于显示异常

public static void main(String[] args) {
    try{
        readFile("d:\\a.txt");
        readFile("d:\\a.jpg");
    }catch(IOException e)
    {//try中抛出什么异常对象,catch就定义什么异常变量,用来接收这个异常对象
        //System.out.println(e.getMessage());
        // 打印:文件后缀不对
        //System.out.println(e.toString());
        //java.io.IOException: 文件后缀不对
        e.printStackTrace();
        //java.io.IOException: 文件后缀不对
        //继续执行的代码  at Exception.Exception3.readFile(Exception3.java:25)
        // at Exception.Exception3.main(Exception3.java:9)
    }
    System.out.println("继续执行的代码");
}
public static void readFile (String filename)throws  IOException
//注意是备注在参数列表后面
{
    if(!filename.endsWith(".txt"))
    {
        throw new IOException("文件后缀不对");
    }
    System.out.println("路径无问题,读取文件");
}

finally代码块

备注在catch的{}后面

无论是否出现异常都会出现

注意

1.必须和try一起使用

2.finally一般用于资源施放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)

try{
    readFile("d:\\a.txt");
    readFile("d:\\a.jpg");
}catch(IOException e)
{
	Exception.Exception3.readFile(Exception3.java:25)
    // at Exception.Exception3.main(Exception3.java:9)
}
finally {
    System.out.println("资源释放");
}

多异常处理

1.多个异常分别处理

2.多个异常一次捕获,多次处理

注意一个try多个catch里面定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上面,否则就会报错

3.多个异常一次捕获,异常处理

//多异常分别处理
try
{
    int[] arr = {1,2,3};
    System.out.println(arr[3]);
}
catch(ArrayIndexOutOfBoundsException e)
{
    System.out.println(e);
}
try
{
    List<Integer> list = List.of(1,2,3);
    System.out.println(list.get(3));
}
catch(IndexOutOfBoundsException e)
{
    System.out.println(e);
}
//java.lang.ArrayIndexOutOfBoundsException: 3
//java.lang.IndexOutOfBoundsException: Index 3 out-of-bounds for length 3
System.out.println("===================");
//多异常一次捕获,多次处理
try{
    int[] arr = {1,2,3};
    System.out.println(arr[3]);
    List<Integer> list = List.of(1,2,3);
    System.out.println(list.get(3));
}
catch(ArrayIndexOutOfBoundsException e)
{
    System.out.println(e);
}
catch(IndexOutOfBoundsException e)
{
    System.out.println(e);
}
//java.lang.ArrayIndexOutOfBoundsException: 3
//因为处理了第一个异常,第二个直接被跳过了
System.out.println("===================");
//多个异常一次处理
try{
    int[] arr = {1,2,3};
    System.out.println(arr[3]);
    List<Integer> list = List.of(1,2,3);
    System.out.println(list.get(3));
}
catch(ArrayIndexOutOfBoundsException e)
{
    System.out.println(e);
}
System.out.println("后续代码");

自定义异常

格式

public class XXXException extends Exception/RuntimeException

{

​ 添加一个空参数构造方法

添加一个带异常信息的构造方法

}

继承Exception就是编译期异常,就必须处理这个异常,要么throws,要么try…catch

继承RuntimeException:就是运行期异常,无需处理,交给虚拟机

public class RegisterException extends Exception{
    public RegisterException()
    {
        super();
    }
    public RegisterException (String message)
    {
        super(message);
    }
}

多线程

并发与并行

并发:2个或多个事件在同一时间段发生

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPgLuexP-1627563797818)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715095056555.png)

并行:2个或多个事件在同一时刻发生

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ci3HlDQ3-1627563797819)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715095132609.png)

线程与进程

进程:指有一个内存中运行的应用程序

线程:进程中的执行单元,负责进程中程序的执行

一个程序运行至少一个进程,一个进程可以包含多个线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCtrzexZ-1627563797819)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715100116806.png)

线程调度

  • 分时调度
    • 所有线程轮流使用CPU的使用权,平均每个线程占用CPU的时间
  • 抢占式调度
    • 优先让优先级高的线程使用CPU,同优先级就随机一个
    • java是抢占式调度

单线程程序:java程序中只有一个线程,从执行main方法开始,从上到下异常执行、

主线程:java中的main

  • 执行主方法的程序

Thread类

  • 多线程用的一个类

1.多线程首先要先创建Thread类的子类

2.重写Thread的run方法 ,设置线程任务

3.创建Thread的子类对象

4.调用Thread中的start方法,开启新线程,执行run方法

​ start()是使该线程开始执行,多次启用一个线程是非法的,哪怕线程已经执行完毕业不能再启动

java属于抢占式调度,优先级高的线程优先执行,一样的随机选择一个执行

public class Main {
    public static void main(String[] args) {
        MyThred mt = new MyThred();
        //创建重写了Thread run 方法的子类对象
        mt.start();
        //开启线程

        for(int i = 0;i<10;i++){
            System.out.println("mimimimi--0--"+i);
        }
    }
}
//这里是使用主函数
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("run"+i);
        }
    }
}//这个程序因为cpu是抢占式的分配,所以打印是随机的
//在执行start的时候,是在栈中多开辟一个空间来运行run方法的

一些Thread的方法

getName():返回该线程的名称

currentThread():返回现在当前正在执行的线程对象的引用

public class MyThred extends Thread{
    @Override
    public void run(){
        //这样重写一下run方法
        System.out.println(getName());
        //里面的getName()是获取线程名称
        //Thread-0
    }
}
public static void main(String[] args) {
    MyThred mt = new MyThred();
    //创建重写了Thread run 方法的子类对象
    mt.start();
    //开启线程
    Thread thread = Thread.currentThread();
    //通过currentThread可以获取当前的线程
    System.out.println(thread.getName());
    //这样就可以打印当前线程的名称了
    //main
}

setName();可以设置线程名称

也可以通过创建带参数的构造方法,参数传递线程的名称

public MyThreadString name){
	super(name);
}
//使用setName来修改线程名称
public static void main(String[] args) {
    MyThred mt = new MyThred();
    //创建重写了Thread run 方法的子类对象
    mt.setName("小强");
    //这里是把线程名称修改为小强
    mt.start();
    //开启线程
}
public static void main(String[] args) {
   MyThred mt = new MyThred("王八");
    mt.start();
}
//这里的实现类时重写了MtThred的构造方法
public class MyThred extends Thread{

    public MyThred(){
    }

    public MyThred(String name){
        super(name);
    }

    @Override
    public void run(){
        //这样重写一下run方法
        System.out.println(Thread.currentThread().getName());
        //里面的getName()是获取线程名称
        //Thread-0
    }
}

Sleep()方法,传入一个毫米数,按对应毫秒值来执行

public static void main(String[] args) {
    for (int i = 0; i < 60; i++) {
        System.out.println(i);
        try{//sleep的作用就是暂停指定毫秒数
            Thread.sleep(1000);
            //暂停1000毫秒
        }catch(InterruptedException e){
            System.out.println(e);
        }
    }
}

Runnable接口

另一种创建线程的方法,以后多使用这个

  • 创建一个Runable接口的实现类
  • 实现类中重写run方法
  • 创建一个Runable接口的实现类对象
  • 创建Thread对象,构造方法中传递Runable接口的实现类对象
  • 调用Thread中的start方法
//1.先创建一个Runable的实现类
public class RunableImpl implements Runnable {
    //2.重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }//设置线程任务
    }
}
public class Runable_study {
    public static void main(String[] args) {
        RunableImpl run = new RunableImpl();
        Thread t = new Thread(run);
        //注意把run方法传进t中
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }//设置main线程任务
    }
}

使用runable接口可以使得实现类可以继承使用其他的类,增加类的功能性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZvaHw39-1627563797820)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715110252917.png)

匿名内部类

new 父类/接口(){

​ 重置父类/接口中的方法

};

public static void main(String[] args) {
    new Thread() {
        //匿名创建Thread
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }.start();
    for (int i = 0; i < 20; i++) {
        System.out.println(Thread.currentThread().getName()+i);
    }//设置主线程
}
//使用接口的匿名
Runnable run = new Runnable(){
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
};
new Thread(run).start();

线程安全

正常的多线程

public static void main(String[] args) {
    //创建实现类对象
    RunableImpl run = new RunableImpl();

    //为了让票源可以共享数据,所以导入同一个实现类

    //创建线程
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();

}
public class RunableImpl implements Runnable{
    private int num = 100;
    @Override
    public void run() {
        while(num>=0){
            System.out.println("我卖出了第"+num+"张票");
            num--;
        }
    }
}

上面的方法会出现的问题就是会出现同时访问的情况

同步代码块

synchronized用于方法的某个区域中,表示只对这个区域的资源实行互斥访问

可以将同步代码块锁住,只让一个线程在同步代码块中执行

synchronized(锁对象){

​ 可能会出现安全问题的代码(访问了共享数据的代码)

}

  • 必须保证多个线程使用的锁对象是同一个
  • 作用
    • 可以同步代码块锁住
    • 只让其在一个线程中运行
private int num = 100;
Object obj = new Object();
//创建一个同步代码块,注意要在run方法外面
@Override
public void run() {
    while(true)
    {
        synchronized (obj) {
            if (num > 0) {
                try {
                    Thread.sleep(10);
                    //这里的sleep是为了提高出现安全问题的概率
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "-->" + num);
                num--;
                //用于卖票
            }
        }
    }
}

原理

  • 使用了一个锁对象
  • 在执行代码块里面的代码前获取试图获取下那个Object的锁对象,但如果有代码使用到这个锁对象就获取不到,所以就使用不到这个代码块了
  • 但是效率会低很多

同步方法

1.将访问了共享数据的代码抽取出来,放到一个方法中

2.在方法上添加synchronized在修饰符后面

//格式
修饰符 synchronized 返回值类型 方法名(参数列表){}

一样是将方法中的代码块锁住

private int num = 100;
//创建一个同步代码块,注意要在run方法外面
@Override
public void run() {
    while(true)
    {
        payTikcet();
    }

}
//这是建立的同步方法
public synchronized void payTikcet()
{
    if (num > 0) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->" + num);
        num--;
        //用于卖票
    }
}

静态同步方法

在前面的synchronized前加个static

锁对象是本类的class对象

public class RunableImpl implements Runnable{

    private static int num = 100;
    //创建一个同步代码块,注意要在run方法外面
    @Override
    public void run() {
        while(true)
        {
            payTikcet();
        }

    }
    //这是建立的同步方法
    public static synchronized void payTikcet()
    {
        if (num > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->" + num);
            num--;
            //用于卖票
        }
    }
}

Lock锁

public class LockRunableImpl implements Runnable{

    private int num = 100;
    //创建一个锁
    Lock l = new ReentrantLock();
    @Override
    public void run() {
        while(true)
        {
            l.lock();
            //放个锁
            if (num > 0) {
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName() + "-->" + num);
                    num--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    l.unlock();
                    //施放锁
                    //这样无论程序是否出现异常都会把锁施放
                }
            }
        }
    }
}

等待与唤醒机制

线程的各种状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvoM7IZ5-1627563797821)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715115305636.png)

wait:使线程进入无限等待,里面也可以传递一个毫秒值,来控制睡眠时间

notify:选取wait set中的一个线程施放

notifyAll:施放所通知对象的wait set上的全部线程

wait和notify必须要由同一个锁对象调用

wait和notify方法必须要在同步代码块或是同步函数中使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KpLzx5pl-1627563797822)(D:\Program Files (x86)]\IDEAProJect\Java入门笔记\Java入门\Java笔记图片\image-20210715133406698.png)

吃包子的小案例

public static void main(String[] args) {
    Object obj = new Object();

    //顾客线程
    new Thread(){
        @Override
        public void run() {
            //保证等待和唤醒只有一个线程
            while (true) {
                synchronized (obj) {
                    System.out.println("告诉老板我要吃包子");
                    //
                    try {
                        obj.wait();
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                    //唤醒之后
                    System.out.println("我醒来了,我开始吃包子");
                    System.out.println("-------------------");
                }
            }
        }
    }.start();

    //老板线程
    new Thread(){
        @Override
        public void run() { 
            while(true) {
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {
                    System.out.println(e);
                }

                synchronized (obj) {
                    //5s之后告知顾客做好了
                    System.out.println("包子好了");
                    obj.notify();
                    //唤醒睡眠的顾客
                }
            }
        }
    }.start();
}

案例升级版

//产品类
public class BaoZi {
    String pi;
    String xian;

    boolean flag = true;
}
//生产商类
public class BaoZiPu extends Thread{
    //先创建一个包子变量
    private BaoZi bz;

    public BaoZiPu(BaoZi baozi){
        this.bz = baozi;
    }

    @Override
    public void run() {
        int count = 0;
        //目的用于生产包子
        while(true){
            synchronized (bz){
                if(bz.flag){
                    try {
                        bz.wait();
                        //调用wait进入等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                //这里是为了有2中包子
                if(count%2==0){
                    bz.pi = "薄皮";
                    bz.xian = "牛肉";
                }else{
                    bz.pi = "鱼皮";
                    bz.xian = "豆腐";
                }
                count++;
                System.out.println("生产包子ing,生产"+bz.pi+bz.xian+"包子");
                try {
                    Thread.sleep(3_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bz.flag = true;
                bz.notify();
                //唤醒顾客
                System.out.println("包子生产完毕");
            }
        }
    }
}
//消费者类
public class ChiHuo extends Thread{
    private BaoZi bz;
    public ChiHuo(BaoZi baoZi){
        this.bz = baoZi;
    }

    @Override
    public void run() {
        while(true){
            synchronized (bz){
                if(!bz.flag){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //唤醒之后就开始吃包子了
                System.out.println("正在吃包子,吃"+bz.pi+bz.xian+"包子");
                bz.flag = false;
                bz.notify();
                //唤醒包子铺
                System.out.println("吃完了,快去做包子");
                System.out.println("--------------------");
            }
        }
    }
}
//主函数
public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        bz.flag = false;
        BaoZiPu pu = new BaoZiPu(bz);
        ChiHuo ch = new ChiHuo(bz);
        pu.start();
        ch.start();

    }

线程池

用一个集合来存放线程

  • 使用线程池的工厂类Executors提供的方法生产一个指定数量的线程池,
  • 创建实现Runable接口的类,设置线程任务
  • 调用Executors中的submit方法,传递线程任务,开启线程
  • 调用shutdowm来销毁线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoo {
    public static void main(String[] args) {
        //调用Excutors里面的静态方法产生一个线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        //调用ExecutorService中的submit方法,传递线程任务,开始线程,执行run方法
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        /*pool-1-thread-2创建一个新线程开始执行
        pool-1-thread-2创建一个新线程开始执行
        pool-1-thread-1创建一个新线程开始执行
         */
        //调用shutdown方法,销毁线程池
        es.shutdown();
    }
}

Lambda语法标准

一些参数

一个箭头

一个代码

格式

(参数列表)->{一些重写方法的代码};

解释说明格式:

():接口中抽象方法的参数列表,没有参数,就空着,有参数就写出参数,多参数用几个逗号分隔

->:传递的意思,把参数传递给方法体{}

{}:重写接口的抽象方法的方法体

public static void main(String[] args) {
    new Thread(()->{
        System.out.println(Thread.currentThread().getName()+"新方法创建了");
    }
    ).start();
}

也可以这样使用

public interface Fish {
    void eat();
}//接口
public class Lambda_study {
    public static void main(String[] args) {
        eatfish(()->{
            System.out.println("I love fish");
        });
        //调用eatfish方法,不传递参数,在方法内调用了接口,在{}重写了fish接口里的eat()方法
    }
    public static void eatfish(Fish fish)
    {
        fish.eat();
    }
}

数据结构

先进后出

队列

先进先出

数组

因为地址连续,所以查询快

但是因为长度固定,所以增删慢

要增删要创建一个新数组把原来数组复制过来

链表

查询慢:每次查询都要从头开始

增删快:对链表结构无影响,所以快

二叉树:分支不超过2个

平衡树:左孩子数量等于右孩子

递归

直接递归:自己调用自己

间接递归:A->B->C->A类似这种

构造方法无法使用递归

递归遍历目录下的文件

public static void main(String[] args) {
    File file = new File("C:\\Users\\啦啦啦\\Desktop");
    FindFile(file);
}
public static void FindFile(File file)
{
    File[] files = file.listFiles();
    for (File file1 : files) {
        System.out.println(file1);
        //判断是不是文件夹,是就遍历
        if(file1.isDirectory())
        {
            FindFile(file1);
        }
    }
}

搜索相应目录下的符号文件后缀的文件

public class File_Find {
        public static void main(String[] args) {
            File file = new File("C:\\Users\\啦啦啦\\Desktop");
            String index  = new Scanner(System.in).next();
            //接收输入的文件类型;
            FindFile(file,index);
        }
        public static void FindFile(File file,String index)
        {
            File[] files = file.listFiles();
            for (File file1 : files) {
                if(file1.isDirectory())
                {
                    FindFile(file1,index);
                }
                else if(file1.getName().endsWith(index))
                {
                    System.out.println(file1);
                }
            }
        }
}

IO流

输入输出流(数据的)

字节流

可以用于读取任意文件

字节的输入

1.创建一个FileOutputStream对象,构造方法传递写入的数据目的地

2.调用FileOutputStream中的write方法

3.释放资源

//1.创建一个FileOutputStream对象,构造方法传递写入的数据目的地
        FileOutputStream fos = new FileOutputStream(("C:\\Users\\啦啦啦\\Desktop\\a.txt"));
        //2.调用FileOutputStream中的write方法
        fos.write(97);
        //将97写入到目录下的文件中
        byte[] btes = {65,66,67,68,69};
        //ABCDE
        fos.write(btes);
        //将btes数组中的数字代表的字符写入到文件中
        fos.write(btes,2,3);
        //输入CDE
        byte[ ] bytes = new Scanner(System.in).next().getBytes();
        //接收来自键盘的输入的字符串
        fos.write(bytes);
        //将接收输入的字符串输入到文件中
        //3.施放资源
        fos.close();

记住上面的文件写入是覆盖式的,每运行一次就会覆盖一次

FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt",true);
//后面的true代表打开续写开关
fos.write("\r\n".getBytes());
        //输入回车
fos.write(new Scanner(System.in).next().getBytes());
fos.close();

字节的输出

创建FileInputStream方法,构造方法中绑定要读取的数据源

len用于获取read读取的有效字节个数

释放资源

//创建FileInputStream方法,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt");
//使用read方法,读取文件
/*int len = 0;
while((len = fis.read())!=-1)
{
    System.out.println(len);
    //一次读取1个字节
}*/
byte[] bytes = new byte[1024];
int len = 0;
//len用于获取read读取的有效字节个数
while((len = fis.read(bytes))!=-1)
{
    System.out.println(Arrays.toString(bytes));
    System.out.println(new String(bytes));
    //将读取到的字符放到bytes数组中
    //再利用Arrays的方法将字节转化为字符串
    //读取的长度取决于byte的长度
}
//释放资源
fis.close();

文件复制器

long e = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt");
//要复制的文件和文件目录
FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\c.txt");
//复制的地点
/*int len=0;
while ((len = fis.read()) != -1)
{   //比较低效地复制
        fos.write(len);
}*/
int len=0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes))!=-1)
{
    fos.write(bytes,0,len);
    //写入len个bytes字节,
}
fos.close();
fis.close();
long o = System.currentTimeMillis();
System.out.println("耗时"+(o-e)+"ms");

字符流

因为中文在不同的定义下是占用不同字节的,正常的按字节读取可能会乱码

所以引入字符流

GBK 2字节

UTF-8 3字节

读取

FileReader fr = new FileReader("C:\\Users\\啦啦啦\\Desktop\\b.txt");
//先创建一个对象使用
int len=0;
/*while((len=fr.read())!=-1)
{
    System.out.print((char)len);
}*/
//因为是一个一个字符接收查询的,所以不会出现字节一样的乱码
char[] ch = new char[1024];
while((len=fr.read(ch))!=-1)
{
    System.out.println(new String(ch,0,len));
    //使用String类里的方法,将字符数组转化为字符串
}
fr.close();

写入

FileWriter fw = new FileWriter("C:\\Users\\啦啦啦\\Desktop\\b.txt",true);
fw.write(97);
//将97写入到内存缓冲区中
fw.flush();
//刷新内存,将内容刷入文件中
String str = new Scanner(System.in).next();
fw.write(str.toCharArray());
//将字符数组写入fw中
fw.write("\r\n");
//输入换行符
fw.write(str.toCharArray(),1,2);
//将字符数组的第一个开始的2个字符输入fw中
fw.close();
//用施放资源也可以将内容刷入文件
//但是施放完后流就不能继续使用了

Properties

是一个双列集合,key和value默认都是字符串

Properties prop = new Properties();
prop.setProperty("low","16");
prop.setProperty("high","18");
prop.setProperty("dog","19");
Set<String> set = prop.stringPropertyNames();
//将properties集合中的键key取出,存放到Set集合中
for (String s : set) {
    String value = prop.getProperty(s);
    System.out.println("key:"+s+" value:"+value);
}

FileWriter fw = new FileWriter("C:\\Users\\啦啦啦\\Desktop\\b.txt",true);
//#my data
//#Mon Mar 29 15:51:30 CST 2021
//打印出来的注释
prop.store(fw,"my data");
//将prop中的数据输入到fw中
prop.store(new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt",true),"");
//使用字节流的话,写中文会出现乱码
Properties prop2 = new Properties();
prop2.load(new FileReader("C:\\Users\\啦啦啦\\Desktop\\b.txt"));
//使用load方法将文件中的键值对读取到prop中
//如果有用#进行注释的话,键值对就不会被读取到prop中
String set2 = prop2.stringPropertyNames();
for (String s : set2) {
    String value = prop2.getProperty(s);
    System.out.println("key:"+s+" value:"+value+"PROP2");
}
fw.close();

缓冲流

字节缓冲输入流

给基本的字节输入流添加一个缓冲区(数组),提高基本的字节读取的效率

(就是在读取的时候,一次返回一个数组,一次拿一堆的数据)

FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt",true);
BufferedOutputStream bos = new BufferedOutputStream(fos);
//创建一个缓冲流对象
bos.write("要写入的对象是我".getBytes());
//将文字导入到bos的内存缓冲区中
bos.flush();
//将内容刷入txt文件
bos.close();
fos.close();

字节缓冲输出流

FileInputStream fis =  new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int len =0;
/*while((len = bis.read())!=-1)
{
    System.out.println(len);
}*/
byte[] bytes = new byte[1024];
while((len=bis.read(bytes))!=-1)
{
    System.out.println(new String(bytes,0,len));
}
//还是使用数组来存放数据更高效
bis.close();

字符缓冲输出流

大部分代码基本和FileWriter差不多

多了个newLine()可以代替\r\n输入回车

BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\啦啦啦\\Desktop\\b.txt",true));
//创建对象
bw.write("\r\n");
//打印回车
bw.write("我爱中国");
bw.newLine();
//也可以用这个来打印回车
bw.write("我是中国人");
bw.flush();
//写入文件
bw.close();

字符缓冲输入流

这个是多了个readLine()可以读取一行的数据

BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\啦啦啦\\Desktop\\b.txt"));
/*String line = br.readLine();
//使用readline读取一行的数据
System.out.println(line);*/
//也可以
String line;
while((line = br.readLine())!=null)
{
    System.out.println(line);
}
br.close();

转化流

因为输入和输出的编码方式不同,可能导致读取的文件内容乱码

所以转化流出现啦!!!

编码表有:utf-8,gbk。。。

OutputStreamWriter

private static void write_gbk() throws IOException{
    FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\c.txt",true);
    OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
    //在创建的对象后面指定要使用的编码表名称
    osw.write("\r\n你好呀");
    osw.flush();
    osw.close();
}

private static void write_utf_8() throws IOException {
    FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\b.txt",true);
    OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
    //在创建的对象后面指定要使用的编码表名称
    osw.write("\r\n你好呀");
    osw.flush();
    osw.close();
}

InputStreamReader

private static void read_gbk() throws IOException{
    FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\c.txt");
    InputStreamReader isr = new InputStreamReader(fis,"gbk");
    //指定使用utf-8格式的方法读取文件
    int len=0;
    char[] ch = new char[1024];
    while((len=isr.read(ch))!=-1)
    {
        System.out.println(new String(ch,0,len));
    }
}

private static void read_utf_8() throws IOException {
    FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\c.txt");
    InputStreamReader isr = new InputStreamReader(fis,"utf-8");
    //指定使用utf-8格式的方法读取文件
    int len=0;
    char[] ch = new char[1024];
    while((len=isr.read(ch))!=-1)
    {
        System.out.println(new String(ch,0,len));
    }
}

序列化

序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常

Serializable接口也叫标记型接口

要进行序列化和反序列化的类必须实现这个接口,会给类加一个标记

有:就可以序列化和反序列化

没有:就会抛出异常

对象的序列化

public static void main(String[] args)throws IOException {
    FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\a.txt",true);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(new Person(" 王狗蛋",18));
    //将Person类的内容写入文件
    oos.close();
}
public class Person implements Serializable {
    private String name;
    private int age;
}

记住静态的变量是不能被序列化的,要是添加一个static在变量前面的话,读取出来的值就是默认值了

对象的反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
    FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\a.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Object o = ois.readObject();
    //使用ois中的方法读取保存的对象
    ois.close();
    System.out.println(o);
    //将o打印出来
}

transient是一个类似static效果的关键字,加上之后的变量也不能被序列化。平时用的话用这个

设置序列号:private static final long serialVersionUID = xxxL;

xxx为任意数字

多个对象的序列化反序列化(用个ArrayList存放)

ArrayList<Person> list = new ArrayList<>();
list.add(new Person("王狗蛋",18));
list.add(new Person("李狗子",20));
list.add(new Person("刘二腿",54));
//先搞一搞list存放Person
FileOutputStream fos = new FileOutputStream("C:\\Users\\啦啦啦\\Desktop\\e.txt",true);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//将集合放入oos中
oos.writeObject(list);
/*=========================================*/
//查看保存的对象
FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\e.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
ArrayList<Person> listcopy = (ArrayList<Person>)o;
//将o强制转化为ArrayList类型
System.out.println(list);
System.out.println("===============");
System.out.println(listcopy);
ois.close();
oos.close();

打印流

PrintStream ps = new PrintStream("C:\\Users\\啦啦啦\\Desktop\\a.txt");
ps.write(97);
//输入a
ps.println(97);
//输入97,这个方法可以输入任意的数据类型
System.setOut(ps);
//把输出的语句的目的地改为ps所在的目的地
System.out.println("i love china");
//这个就不会在控制台输出了
ps.close();

网络编程

软件结构

C/S:客户端服务器结构

B/S:浏览器服务器结构

网咯通信协议

通信的双方需要对一些数据方面的问题进行一些规定才能开始数据交换

TCP/IP协议:internet最基本的协议

协议分类

UDP:面向无连接的协议,就是相互之间没有联系,发送的只管发送,不管送不送得到的。 (数据限制在64kb以内)

TCP:面向连接的协议,可以提供计算机之间无差错的数据传输

要明确客户与服务器,要经理三次握手:

一:客户端发送连接请求

二:服务器向客户端发送响应,通知客户端收到

三:客户端再次发送确认信息,确认连接

IP地址

常用命令

查看本机ip地址

ipconfig

检查网络是否连通

ping 空格 IP地址

在控制台输入

特殊IP地址

127.0.0.1------自己的地址

localhost:自己的ip地址的域名

端口号

一个逻辑端口,网络软件打开时会被计算机赋予的一个随机端口号,或者是是软件在打开时同系统要的指定的端口号

(由2个字节组成,取值在0~65535之间)

1024之前的端口号不能用,已经被分配了

网络软件的端口号不能重复

常用端口号:

1.80端口,网络端口 如www.baidu.com:80正确网址

2.数据库:MYSQL:3306,oracle:1521

3.Tomcat服务器:8080

TCP通信程序

Socket类(客户端类)

主要有:OutputStream getOutpustStream(),构造方法绑定服务器IP地址和端口号

​ InputStream getInputStream()返回此套接字的输入流

​ void close() 关闭此套接字

实现步骤:

​ 1.创建一个客户端Socket,绑定服务器IP地址和端口号

​ 2.Socket使用 getOutpustStream()获取字节输出流OutputStream对象

​ 3.使用网络字节输出流OutputStream对象中的 write方法,给服务器发送数据

​ 4.使用getInputStream()获取网络字节输入流InputStream对象

​ 5.使用InputStream对象中的read方法,读取服务器回写的数据

​ 6.释放资源

1.客户端与服务器进行交互,必须使用Socket提供的网络流,不能用自己的流对象

2.如果与服务器的三次握手中,服务器没有启动,就会抛出异常

		Socket socket = new Socket("127.0.0.1",8888);
        //这里使用的IP地址是自己电脑的
        OutputStream os = socket.getOutputStream();
        os.write("你好服务器".getBytes());
        //给服务器发送数据
        /*现在还没有服务器,所以这一段代码运行不了*/
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        socket.close();

ServerSocket类(服务器类)

方法:Sokcet accept()倾听并接收此套接字的拼接

服务器实现步骤:

​ 1.创建服务器ServerSocket对象和系统要指定的端口号

​ 2.使用ServerScoket中的accep方法,获取客户端

​ 3.使用getInputStream()获取网络字节输入流Inputstream

​ 4.使用InputSream中的read方法,获取客户端发送的数据

​ 5.使用getOutputstream方法获取outputstream对象

​ 6.使用OutputStream对象中的write方法,给客户端回写数据

​ 7.施放资源

ServerSocket server = new ServerSocket(8888);
//1.创建服务器ServerSocket对象和系统要指定的端口号
Socket socket = server.accept();
//2.使用ServerScoket中的accep方法,获取客户端
InputStream is = socket.getInputStream();
//3.使用getInputStream()获取网络字节输入流Inputstream
byte[] bytes = new byte[1024];
int len = is.read(bytes);
//4.使用InputSream中的read方法,获取客户端发送的数据
System.out.println(new String(bytes,0,len));
OutputStream os = socket.getOutputStream();
// 5.使用getOutputstream方法获取outputstream对象
os.write("收到谢谢".getBytes());
// 6.使用OutputStream对象中的write方法,给客户端回写数据
socket.close();
server.close();

一个文件上传服务器,服务器下载的案例

服务器

public class FileServerScoket {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        //1.创建服务器ServerSocket对象和系统要指定的端口号
        Socket socket = server.accept();
        //2.使用ServerScoket中的accep方法,获取客户端
        InputStream is = socket.getInputStream();
        //获取网络的字节输入流到is中
        File file = new File("C:\\Users\\啦啦啦\\Desktop\\Copy");
        if(!file.exists())
        {
            file.mkdirs();
            //创建一个文件夹
        }
        FileOutputStream fos = new FileOutputStream(file+"\\2.md");
        //创建一个绑定了复制路径的对象
        int len = 0;
        byte[] bytes = new byte[1024];
        //下面使用的read方法读取客户端上传的文件,一直读取不到文件的结束标记
        //read方法进入堵塞状态,一直死循环
        //后面的代码不会执行到
        //客户端的read同理,也读取不到服务回写的内容
        //没有结束标记是因为在上传的时候就没有把结束标记上传(在结束标记出现时就停止上传了)
        while((len=is.read(bytes))!=-1)
        {
            fos.write(bytes,0,len);
        }
        //复制内容到本地
        socket.getOutputStream().write("Ok 上传".getBytes());
        fos.close();
        socket.close();
        server.close();
    }
}

客户端

public class FileScoket {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",8888);
        //创建客户端对象
        FileInputStream fis = new FileInputStream("C:\\Users\\啦啦啦\\Desktop\\学习周记.md");
        //创建文件读取对象
        OutputStream os = socket.getOutputStream();
        int len = 0;
        byte[] bytes = new byte[1024];
        while((len=fis.read(bytes))!=-1)
        {
            os.write(bytes,0,len);
            //将文件写入服务器中
        }
        InputStream is = socket.getInputStream();
        socket.shutdownOutput();
        //用这个上传一个结束标记
        while((len = is.read(bytes))!=-1)
        {
            System.out.println(new String (bytes,0,len));
            //使用InputStream对象中的read方法,读取服务器回写的数据
        }
        fis.close();
        socket.close();
    }
}

服务器优化版本

ServerSocket server = new ServerSocket(8888);
 //1.创建服务器ServerSocket对象和系统要指定的端口号
 while(true)//增加了循环,让服务器可以一直接收文件
 {
     Socket socket = server.accept();
     //2.使用ServerScoket中的accep方法,获取客户端
     /*添加了多线程,让服务器可以同时接收多个文件
     还用try catch将内容声明    */
     new Thread(new Runnable() {
         @Override
         public void run() {
             try{
                 InputStream is = socket.getInputStream();
                 //获取网络的字节输入流到is中
                 File file = new File("C:\\Users\\啦啦啦\\Desktop\\Copy");
                 if(!file.exists())
                 {
                     file.mkdirs();
                     //创建一个文件夹
                 }
                 //后期优化一下
                 /*FileOutputStream fos = new FileOutputStream(file+"\\2.md");*/
                 String filename = "\\网络下载"+ System.currentTimeMillis()+new Random().nextInt(99999)+".md";
                 //创建一个随机的名称,防止文件重复
                 FileOutputStream fos = new FileOutputStream(file+filename);
                 //创建一个绑定了复制路径的对象
                 int len = 0;
                 byte[] bytes = new byte[1024];
                 //下面使用的read方法读取客户端上传的文件,一直读取不到文件的结束标记
                 //read方法进入堵塞状态,一直死循环
                 //后面的代码不会执行到
                 //客户端的read同理,也读取不到服务回写的内容
                 //没有结束标记是因为在上传的时候就没有把结束标记上传(在结束标记出现时就停止上传了)
                 while((len=is.read(bytes))!=-1)
                 {
                     fos.write(bytes,0,len);
                 }
                 //复制内容到本地
                 socket.getOutputStream().write("Ok 上传".getBytes());
                 fos.close();
                 socket.close();
             }catch(IOException e)
             {
                 System.out.println(e);
             }
         }
     }).start();
 }
//因为服务器一直开启,所以不用close了
 // server.close();

B/C方法服务器

//可以在网页输入  http://127.0.0.1:8080/(文件地址(html文件的),让服务器打开)
public class ServerSocket2 {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        while(true)
        {
            try{
                Socket socket = server.accept();
                InputStream is = socket.getInputStream();
                byte[] bytes = new byte[1024];
                int len=0;
        /*while((len=is.read(bytes))!=-1)
        {
            System.out.println(new String(bytes,0,len));
        }*/

                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                //把is转化为字符缓冲输出流
                //把客户端请求信息的地址读取出来,在回复的第一行
                String line = br.readLine();
                //切割一下,获取文件的路径
                String[] arr = line.split(" ");
                String htmlpath = arr[1].substring(1);

                FileInputStream fis = new FileInputStream(htmlpath);
                //用fis读取路径文件
                OutputStream os = socket.getOutputStream();

                //一些以后学HTML的
                os.write("HTTP/1.1 200 OK\r\n".getBytes());
                os.write("Content-Type:text/html\r\n".getBytes());
                //要写空行
                os.write("\r\n".getBytes());

                while((len = fis.read(bytes))!=-1)
                {
                    os.write(bytes,0,len);
                }

                fis.close();
                socket.close();
            }catch (IOException E)
            {
                System.out.println(E);
            }
        }
        //server.close();
    }
}

可以用于打开网页

函数式接口

有且仅有一个抽象方法的接口(Lambda)

定义格式

修饰符 interface 接口名称{
 public abstract 返回值类型 方法名称(参数);
 //其他非抽象方法内容
}
@FunctionalInterface
//使用这个注解来检测是不是函数式接口
public interface MyFunctionaInterface {
    public abstract void method();
}

函数式编程

Lambda的延迟执行

有些场景下代码执行后不一定有用,所以Lambda可以作为解决方案

public static void main(String[] args) {
    String masg1="你好";
    String masg2 = "中国";
    //调用showLog方法
    showLog(2,()->{return masg1+masg2;});
    //使用lambda表达式,仅仅是把参数传递到showlog中
    //只有满足条件,才会执行{}里面的字符串拼接函数
}
public static void showLog(int level,MyFunctionaInterface mb)
{
    //对日志的等级进行判断,如果是1级,调用接口中的方法
    if(level==1)
    {
        System.out.println(mb.builderMessage());
    }
}

在这里使用正常的方法执行的话,如果不符合if条件,会白白拼接一次字符串。

用了lamdba方法后,因为延迟执行了,所以只有在满足条件的情况下才会执行调用到参数的语句。

Supplier接口

生存型接口,指定接口中的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据

public class Offen_FunctionInterface1 {
    public static String getString(Supplier<String> sup)
    {
        return sup.get();
    }//方法在执行get方法的时候就会返回定义的泛型

    public static void main(String[] args) {
        String s = getString(()->{
            return "早上好";
        });
        System.out.println(s);
    }
}

有关Lambda的学习先跳过一下,理解有点困难,先看后面的内容

Stream流

List<String> list = new ArrayList<>();
list.add("我爱罗");
list.add("林泽霖");
list.add("林颖超");
list.add("刘贝");
list.stream()
        .filter(name->name.startsWith("林"))
        .filter(name->name.length()==3)
        .forEach(name-> System.out.println(name));
//流的方法,分别筛选林开头和字数为3的名字

一个小小的基础代码

更多的类似一个流水线,过程一步一步走的

流基本都是对数组和集合的元素进行的一个流水线操作(想象下遍历)

获取流

1.所有的Collction集合都有Stream流的默认获取

2.Stream接口的静态方法of可以获取数组对应的流

List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();

Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();

Map<String,String> map = new HashMap<>();
Set<String> key = map.keySet();
Stream stream3 = key .stream();

Stream stream4 = Stream.of(1,2,3,4,5);

常用方法

forEach

void forEach(Consumer<? super T>action);

接收一个Consumer接口的函数,会将每一个流元素交给Consumer处理

是一个终结方法,返回值不是Stream类型,调用后不能再继续调用Stream中的其他方法

作用:用来遍历流中的数据

Stream<String> stream = Stream.of("张三","李四","王五");
stream.forEach((String name)->{
    System.out.println(name);
});

filter

可以将一个流转化为另一个子集流

Stream<String> stream = Stream.of("张三","李四","王五","赵六","田七","张九龄");
Stream stream2 = stream.filter((String name)->{return name.startsWith("王");});
//这个方法返回的还是一个stream流,可以用一个新的stream来接收
stream2.forEach((name)->{
    System.out.println(name);
});

map

如果要将流中的元素映射到另一个流中去,用map方法

<R> Stream <R> mapp(Function<? super T,? extends R>mapper);

可以将R类型的数据转换为T类型的数据

Stream<String> stream = Stream.of("1","2","3","4");
//使用map方法
Stream<Integer> stream2 = stream.map((String s)->{
    return Integer.parseInt(s);
    //将s转化为整数
});
stream2.forEach(i-> System.out.println(i));

count

long count();
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    Stream<Integer> stream = list.stream();
    long count = stream.count();
    System.out.println(count);
}

limit

Stream<T> limit(long maxSize);

对流中的元素进行截取

Stream<String> stream = Stream.of("狗","人","鸡","鸭","鹅");
//使用limit方法截取
Stream<String> stream2 = stream.limit(3);
stream2.forEach((String s)->{
    System.out.println(s);
});
//截取前3个元素

skip

跳过前面几个元素

Stream<String> stream = Stream.of("狗","人","鸡","鸭","鹅");
Stream<String> stream2 = stream.skip(2);
    stream2.forEach(s-> System.out.println(s));
    //跳过前面2个元素

concat

组合方法

Stream<String> stream1 = Stream.of("狗","人","鸡","鸭","鹅");
Stream<String> stream2 = Stream.of("张三","李四","王五","赵六","田七","张九龄");
Stream<String> stream = Stream.concat(stream1,stream2);
stream.forEach(s-> System.out.println(s));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值