Java学习日志


begin:
	以下为我对于Java的一些见解,供日常复习而用

前言

[API文档](Java 8 中文版 - 在线API中文手册 - 码工具 (matools.com))

1.关于计算机的基础知识

  1. 硬件及冯·诺依曼结构

结构图

  1. 计算机软件

  2. 计算机软件可以使计算机按照事先约定好的顺序完成特定的功能

    计算机软件是操作计算机硬件

    按功能可分为系统软件和应用软件

2. 计算机快捷键

CTRL + C :复制

CTRL + V :粘贴

CTRL + A :全选

CTRL + X :剪切

CTRL + X :撤销

CTRL + S :保存

Alt + F4 :关闭当前窗口

CTRL + shift + Esc :打开任务管理器

shift + delete :删除文件

Alt + tab :切换界面

3. 打开cmd

常用的dos命令

通过/d实现跨盘符的切换
#盘符切换					cd D:	|| 	 cd /d d:		(change directory)
#查看当前目录下的所有文件	   dir
#切换目录					cd+当前目录下的文件路径
#返回上一级目录			  cd ..
#清理屏幕					cls (clear screen)
#退出终端					exit
#显示IP地址					ipconfig

#打开应用
	calc (打开计算器)

    mspaint(打开画图软件)

    notepad(打开记事本)
    
#ping 命令
	ping www.baidu.com	得到百度的IP地址
	
#文件操作
	md(make directory) text		创建目录
	rd(remove diretory) text	移除目录
	cd>文件名					  创建文件
	del 文件名 				  删除文件

Java基础

学习过程中推荐Java API文档

1. 面向对象开发方法的一些核心思想与概念


​ 面向对象分析与设计

  • 建立模拟问题领域的对象模型

  • 自底向上的抽象

    1. 把问题领域中的事物抽象为具有特定属性行为的对象
    2. 把具有相同属性行为的对象抽象为类
    3. 将具有共性的类的共性抽象到父类
  • 自顶向下的分解

    对象(Object)

    类(Class)和类型(Type)

    消息(Message)和服务(Service)

    接口(Interface)

    抽象(Abstract)

    封装(Encapsulation)

    继承(Inheritance)

    多态(Polymorphism)

2. java 入门


2.1 java注释

//注释内容			单行注释	ctrl+/ 可进行多行的单行注释
/*注释内容*/		多行注释
/**
 *@auther 	作者名
 *@version	版本号
 *@since	指明需要最早使用的jdk版本
 *@param	参数名
 *@return	返回值情况
 *@throws	异常抛出情况
 *
 */			JavaDoc:文档注释(/**+回车)
 加在类上面就是类的注释,加在方法上面就是方法的注释

用命令行创建JavaDoc文件
javadoc -encoding(编码) UTF-8 -charset(字符集编码) UTF-8 java文件 //多显示一点中文

tips: 使用IDEA生成javadoc文档

2.2 标识符

Java所有的组成部分都需要名字。类名,变量名以及方法名

所有的标识符都应该以字母(AZ或者az),美元符($),或者下划线(_)开始

不能使用关键字作为变量名或者方法

标识符是大小写敏感的

2.3 数据类型

定义字符串:String s = "abc"
  • 基本数据类型

    1位(byte)=8字节(bit)

    1 bit表示11 Byte表示一个字节 1B=8b
    1024B=1KB
    1024KB=1MB
    1024MB=1GB
    数据类型 默认值 字节数
    byte 0 1byte
    short 0 2byte
    int 0 4byte
    long 0L 8byte
    float 0.0f 4byte
    double 0.0d 8byte
    char ‘\u0000’ 2byte
    boolean false 1byte

    long数据类型和float类型定义

    long i = 1234L;
    float j = 123.3f;

    数据类型扩展

    整数:		进制		二进制0b		十进制		八进制0	十六进制0x
    int i=10;
    int i=010;	//八进制0
    int i=0x10;	//十六进制0x	0~9 A~F 16
    
    浮点数:有限		离散		舍入误差	大约		接近但不等于
        (最好完全避免使用浮点数进行比较)
        银行业务使用BigDecimal	数学工具类
        
    字符类型	(所有的字符本质还是数字)(编码使用 Unicode 2字节 0~65536)
      char c1='a';
      char c2='中';
      System.out.println((int)c1);  //强制类型转换
    
    转移字符
        // \t 制表符
        // \n 换行符
  • 引用数据类型

    1. 接口
    2. 数组

2.4 变量作用域

public class Variable{
    static int a=0;					//类变量   static;从属于类
    String str = "hello world";		//实例变量;从属于对象;如果不进行初始化,则变成这个类型的默认值;除了基本数据类型,其余的默认都是NULL
    
    public void method(){
        int i=0;			//局部变量;必须声明和初始化值
    }
}

常量初始化后不能再改变的值

常量名一般使用大写字母

final 常量名=值;
final double PI=3.14;

final在类加载时只进行一次初始化,在类内部的值也不再改变
static意思是静态,在类加载时完成,在一个类里面只有一个值,在类的内部可以修改多次

2.5 自增自减

int a = 3;
int b = a++;		执行完这行代码后,先给b赋值,再++
int c = ++a;		执行完这行代码前,先++,再给b赋值

2.6 位运算(了解)

A = 0011 1100
B = 0000 1101
    
A&B = 0000 1100		(上下比较,两个都为1,才为1,否则为0)
A|B = 0011 1101		(上下比较,都为0结果才为0,否则为1)
A^B = 0011 0001		(相同为0,否则为1)
~B  = 1111 0010		(与B完全相反)
    
<<  左移	*2		2<<3	16
>>  右移	/2
    0000 0010    2
    0001 0000    16

2.7 字符串连接符 (+)

“+”号运算符两侧,只要出现String类型,如果另一侧不是字符串,则会将其转换为字符串再连接起来

System.out.println(""+a+b);		字符串在前面,直接进行拼接
System.out.println(a+b+"");		字符串在后面,a+b先进行计算,再拼接

2.8 三元运算符

x ? y : z 		//如果x==true,则结果为y,否则为z

2.9 包机制

package pkg1.pkg2.pkg3
一般利用公司域名的倒置作为包名
such: package com.baidu.www

import 包的路径		//导入包

2.10 switch多选择结构

switch(变量类型)		//变量类型可以是byte,short,int,char,String类型
{
        //case穿透,不写case会一直执行下面的语句直到遇到case
	case value:			//case标签必须是字符串常量或字面量
	//语句
	break;//可选
	case value:
	//语句
	break;//可选
	default://可选
		//语句
}

2.11 break和continue

break :强行退出循环,不执行循环中剩余的语句
continue : 用于终止某次循环过程,跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定

2.12 打印三角形

public class Main {
    public static void main(String[] args) {
        for(int i=1;i<=5;i++){
            for(int j=5;j>i;j--){
                System.out.print(" ");
            }
            for(int j=1;j<=i;j++){
                System.out.print("*");
            }
            for(int j=1;j<i;j++){
                System.out.print("*");
            }
            System.out.println();
        }
    }
}

2.13 方法

System.out.println()	//调用系统类中标准输出对象out的println方法
方法是语句的集合,它们在一起执行一个功能
    1. 方法包含于类或对象中
    2. 方法在程序中被创建,在其他地方被引用
    
方法的定义: 
	修饰符 返回类型 方法名 (参数类型 参数名){
        ···
        方法体
        ···
        return 返回值;   //返回值  , 终止方法
    }
方法重载
	方法名称必须相同
    参数列表必须不同
    方法的返回类型可以相同也可以不同
    tips: 仅仅返回类型不同不足以成为方法的重载
        
方法名称相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配对应的方法
        
        Java传参为值传递

2.14 命令行传递参数

public static void main(String[] args){
	for(int i=0;i<args.length;i++)
		System.out.println("args["+i+"]: "+args[i]);
}

javac 路径名    //编译为.class文件
java 路径名 参数	 //注意要加上包的路径	
such:
	java h1.Main this is hh
        
result:
args[0]:this
args[1]:is
args[2]:hh
    

2.15 可变参数

在方法的声明中,可在指定参数类型后面加一个省略号()
一个方法只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在它之前声明
//本质上类似于命数组传参
public class Demo04{
	public static void main(String[] args){
        Demo04 demo04 = new Demo04();
        demo04.test(1,2,3,4)
    }
    public void test(int... i){ 		//类似于int类型的数组
        
    }
}

2.16 递归

递归结构包括两个部分:
	递归头:什么时候不带哦一年自身的方法,如果没有头,将陷入死循环
	递归体:什么时候需要调用自身的方法
    
    
理解:就是有个“递”和“归”的过程
    important :明确这个函数的功能是什么

2.17 数组

数组定义:数组是相同类型数据的有序集合

//变量的类型     变量的名字   =    变量的值

数组也是对象,数组元素相当于对象的成员变量

数组的定义
    声明数组
int[] nums;		//首选的方法
int nums2[];	//效果相同,但不是首选方法

//静态初始化
int[] a = {1,2,3,4,5,6,7,8};

	使用new来创建数组(开辟了一段空间)
//动态初始化:包含默认初始化
int[] nums = new int[arrysize];		//这里面可以存放arrysize个int类型的数据
nums[0]=10;

数组的元素是通过索引访问的,数组索引从0开始
    
    
 	二维数组
    int[] a = {1,2,3};
    int[][] nums= {{1,2},{2,3},{3,4}};
	实际上相当于将原来的一维数组的每一个数字嵌套

二维数组图示

2.18 Java内存分析

Java内存:
堆:
    存放new的对象和数组
    可以被所有的线程共享,不会存放到别的对象引用
    
栈:
    存放基本变量类型(会包含这个基本数据类型的具体数值)
    引用对象的变量(会存放这个引用在堆里面的具体地址)
    
方法区:
    可以被所有的线程共享
    包含了所有的class和static变量

2.19 for each

//遍历数组中的所有元素

int[] arrays = {1,2,3,4,5}
	//没有下标
	for(int array : arrays){
        System.out.println(array);
    }

3. 类和对象

以类的方式组织代码,以对象的方式封装数据


3.1 Scanner 对象

需要导入包:
import java.util.Scanner;
基本语法:
Scanner s = new Scanner(System.in);
//创建一个扫描器对象,用于接收键盘数据
Scanner s = new Scanner(System.in);

//凡是属于IO流的类如果不关会一直占用资源
S.close();
Scanner类的next()nextLine()方法
next()方法:
    对输入有效字符之前遇到的空白,next()方法会将其自动去掉
    只有输入有效字符后才将后面输入的空白作为分隔符或者结束符
    不能得到带有空格的字符串
    
    若想输入一个字符,即可
    Scanner scanner = new Scanner(System.in);
	char c = scanner.next().charAt(0);


nextLine()方法: 
	该方法返回输入回车之前的所有字符,包括空格
//循环输入数据
while(scanner.hasNextDouble())
{
	double x = scanner.nextDouble();
}

3.2 一般类的组成

类是一个模板,对象是一个具体的实例
    类:
    	静态的属性    属性
    	动态的行为    方法
    
public class Student{
    成员变量
方法:
    成员方法
    构造方法(构造器)
}

static 可以修饰成员变量和成员方法,是和类一起加载的,static修饰的成员变量为类变量
没用static修饰的只有在类实例化之后才存在,static修饰的方法为静态方法
    
非静态方法只跟对象有关
    
this 当前对象的引用
    
    
匿名对象:在调用多次方法的时候,只能调用唯一一次		//省略了对象名称
    	如果希望同一个对象调用多次方法,那么必须给对象起个名字

3.3 构造器

构造器:
    1.和类名相同
    2.没有返回值
作用:
    1.new 本质在调用构造方法
    2.初始化对象的值
注意点:
    定义有参构造后,如果想使用无参构造,需要显示的定义一个无参的构造器
    每个类都会有默认构造器
    
alt + insert : 可生成一个构造器
子类new一个对象的同时也new了一个父类对象
super注意点:
    super调用父类构造器必须要在子类构造器的第一行
    super必须只能出现在子类的方法或者构造方法中
    super和this不能同时调用构造方法
    
super和this的不同:
    代表的对象不同:
    	this:本身调用这个对象
    	super:代表父类对象的引用
    前提:
    	this:没有继承也能使用
    	super:只能在继承条件下才可以使用 
    构造方法:
    	this() :本类的构造
    	super():父类的构造
    
public class Student extends Person{    
    public Student(){
 		super();	//调用父类构造器
        System.out.println("构造器执行了");
	}
}

除了用对象引用来访问对象成员外,还可以直接使用创建的对象本身来引用对象成员,格式如下:
    new 类名().对象成员
    创建后只能访问其中某一个成员,不能像对象引用那样可以访问多个对象成员,在完成某一个对象成员的访问后,该对象就会变成垃圾对象

3.5 Java访问控制四个修饰符

同一类中 同一包中 不同包子类 全局范围
public
protected ×
default(一般都是默认) × ×
private × × ×

tips :default并不是关键字”default”,而是根本不写

3.6 static

static只执行一次,与类一起加载  

3.7 abstract

抽象类的所有方法,继承了它的子类,都必须要实现它的抽象方法(除非那个子类也是抽象类)
    
    不能new抽象类,只能靠子类去实现它
    抽象类中可以写普通的类
    抽象方法必须要在抽象类中

3.8 接口

接口:只有方法的定义,没有方法体
    接口中的方法用public abstract修饰
    接口中作为常量时用public static final
    接口是更高级别的抽象,若不定义为常量,若有多个类实现该接口,其中一个类改变接口中的变量,其他类的也要跟着改变,容易
    
    
使用 interface 来定义接口
    implements 来实现接口
    
实现了接口的类,就需要重写接口中的方法
    接口可实现多继承
    
//定义接口
public interface UserService{
	[public] [static] [final] 常量类型 常量名 = 常量值;
    [public] [abstract] 方法返回值类型 方法名 (参数列表);
    
    //jdk 8之后
    //在方法名前面加个 default 关键字即可实现默认方法
    [public] default 方法返回值类型 方法名(){
        //默认方法的方法体
    }
    [public] static 方法返回值类型 方法名(){
        //静态方法(类方法)的方法名
    }
}

//实现接口
public class Student implements UserService,TimeService{
    
}

静态方法可通过"接口名.方法名"
抽象方法和默认方法只能通过接口实现类的实例对象来调用
    
接口中不能含有静态代码块但含有静态方法,而抽象类可以有静态代码块和静态方法
    因为静态代码块中只含有表达式,而且接口是方法的集合体,这违背了它的基本原理
    
与抽象类相比,接口不能搞定的:
	抽象类能够定义非 static final 的属性(field),而接口不能。接口的属性都是static final的。
	抽象类能够定义非 public方法,而接口不能。接口的方法都是public的。
    
与接口相比,抽象类不能搞定的:
	接口可以多继承(实现),而抽象类不能。抽象类只能单继承。

tips: 当一个类实现接口时,如果这个类是抽象类,只需实现接口中的部分抽象方法即可,否则需要实现接口中所有抽象方法

个人理解:
    非抽象类继承抽象类或者实现接口时,必须实现(重写)抽象类的所有抽象方法
    抽象类实现接口时,可以只实现(重写)接口中的部分抽象方法
    
    
    基于上述,我们可以这样做,当你只想实现接口中的个别方法(不是所有方法)时,你可以先写一个抽象类来实现该接口,并在抽象类中实现除了你想要的方法之外的所有方法(方法体为空)。接着再用你的类继承这个抽象类,这个类中就只用实现你需要的方法了,这样就可以达到你的需要了,我们也可以不用管其他方法具体怎么实现(因为我们用不到那些方法)

3.9 内部类

内部类可获得外部类的私有属性

public class Outer{
    private int id;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    
    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }
    }
}


public static void main(String[] args){
    Outer outer = new Outer();
    //通过这个外部类来实例化内部类
    Outer.Inner inner = outer.new Inner();
}
成员内部类
    成员内部类可以访问外部类中所有的成员,同时外部类也可以访问成员内部类的所有成员 
    
    创建内部类的具体语法格式如下:
    外部类名.内部类名 变量名 = new 外部类名().new 内部类名();  //外.内

	如果出现了重名现象,那么格式是,外部类名称.this.外部类成员变量名
局部内部类
    也叫方法内部类,定义在某个局部范围的类
    局部内部类可以访问外部类中所有成员,而只有当前所属的方法才能使用它,出了这个方法外面就不能用了
    
    如果希望访问所在方法的局部变量,那么这个局部变量必须是 final 修饰的
    
    备注:从Java 8开始,只要局部变量事实不变,那么 final 关键字可以省略
    
    原因:
    1.new出来的对象放在堆内存当中
    2.局部变量是跟着方法走的,在栈内存当中
    3.方法运行结束之后,立刻出栈,局部变量就会跟着消失
    4.但是new出来的对象会在堆中持续存在,直到垃圾回收消失
静态内部类
    只能访问外部类的静态成员,同时通过外部类访问内部类成员时,可以跳过外部类直接通过内部类访问静态内部类成员
    创建静态内部类对象的基本语法格式如下:
    外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名
匿名内部类				//省略了实现类/子类名称
    适合创建后只使用一次的类,创建匿名内部类时须继承一个已有的父类或者实现一个接口
    
    匿名内本身无名,创建时不存在构造方法,不能重复使用
//父类/接口构造方法 + 类体 = 匿名内部类
匿名内部类的定义格式
接口名称 对象名 = new 接口名称(){		//这里的new可理解为创建了一个匿名内部类对象
  	//覆盖重写所有抽象方法  
};

//使用了匿名内部类,而且省略了对象名称,也是匿名对象
new 接口名称(){
  	//覆盖重写所有抽象方法  
};



//定义一个接口
public interface MyInterface {
    void method();
}

//接口实现类
public class MyInterfaceImpl implements MyInterface{
    @Override
    public void method() {
        System.out.println("匿名内部类实现了方法");
    }
    
}

public class DemoMain {
    public static void main(String[] args) {
        //使用匿名内部类
        MyInterface obj = new MyInterface() {		//MyInterface是接口名称,表明匿名内部类实现该接口
            @Override								//{ }之间的内容就是接口实现类中的内容	
            public void method() {
                System.out.println("匿名内部类实现了方法");
            }
        };
    }
}

3.10 包装类

基本类型也字符串类型之间的相互转换
基本类型->字符串(String)
    1.基本类型的值+""   最简单的方法(工作中常用)
    	int i1 = 100;
		String s1 = i1 + "";
		System.out.println(s1 + 200);	//100200
    
    2.包装类的静态方法toString(参数),是Object类toString方法的重载
    	static String toString(int i) 返回一个表示指定整数的String对象
        
        String s2 = Integer.toString(100);
		System.out.println(s2 + 200);	//100200

    3.String 类的静态方法valueOf(参数)
    	static String valueOf(int i) 返回int参数的字符串表示形式
        
        String s3 = String.valueOf(100);
		System.out.println(s3 + 200);	//100200
    
字符串(String)->基本类型
    使用包装类的静态方法parseXXX("字符串");
		Integer 类:static int parseInt(String s)
        Double  类:static double parseDouble(String s)
            
自动装箱和自动拆箱可使运算更加方便

4. 常用类


4.1 Arrays类(数组的工具类)

方法均为static修饰,使用时可直接使用类名进行调用
toString 方法 		打印出数组的所有元素
sort方法				升序排列

4.2 Random类

1. 导包
    import java.util.Random
    
2. 创建
    Random r = new Random();//小括号中留空即可
    
3. 使用
    获取一个随机的int数字(范围是int所有范围,有正负两种),int num = r.nextInt();
	获取一个随机的int数字(参数代表了范围,左闭右开区间),int num = r.nextInt(3);
	实际上代表的含义是[0,3),也就是0~2
              
    若想获取[1.n]
        可用int num = r.nextInt(n)+1;

5. Java三大特性


5.1 封装

程序设计应当追求"高内聚,低耦合"
    高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;
    低耦合:仅暴露少量的方法给外部使用
封装(数据的隐藏):
    通常下禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这这称为信息隐藏
    
    属性私有,通过getter和setter方法来进行属性值的更改
    
意义:
    1.提高程序的安全性,保护数据
    2.隐藏代码的实现细节
    3.统一接口
    4.提高系统可维护性

5.2 继承

继承本质是对某一批类的抽象,从而实现对现实世界更好的建模
    是类和类之间的一种关系
    
extends意思是扩展,子类是父类的扩展
    

//父类
public class Animal{
    
}

//派生类或子类
public class dog extends Animal{
    
}
子类继承了父类,就会拥有父类的全部方法
    
ctrl + H 显示继承类的关系图

tips: Java类中只有单继承,没有多继承

方法重写
    
    静态方法由static修饰,只与类有关
    非静态方法与对象有关,与类无关
    
   //父类
public class Animal{
    public void test(){
        System.out.println("这是父类");
    }
}

//派生类或子类
public class dog extends Animal{
    //Override  重写
    System.out.println("这是子类");
}


重写需要有继承关系,子类重写父类的方法!
    1. 方法名必须相同
    2. 参数列表必须相同
    3. 修饰符:范围可以扩大,但不能缩小
    4. 抛出的异常:范围可以缩小,不能扩大
    
为什么需要重写:
    父类的功能,子类不一定需要,或者不一定满足

tips:

对于继承关系,新建一个子类对象时,都是先创建一个父类对象,在创建子类对象
若父类只有有参的构造方法,则子类必须要显示定义一个有参的构造方法

5.3 多态

多态:同一方法可以根据发送对象的不同而采用多种不同的行为方式
    1.多态时方法的多态,属性没有多态
    2.父类和子类有联系
    3.存在继承关系,方法需要重写,父类引用指向子类对象
    
instanceof关键字,如果匹配,则可以进行类型之间的转换
       //父类
public class Animal{
    public void test(){
        System.out.println("这是父类");
    }
}

//派生类或子类
public class dog extends Animal{
    //Override  重写
    System.out.println("这是子类");
}

Animal animal = new dog();

tips:以下修饰符修饰的子类不能重写:

1.static 方法:属于类,它不属于实例
   2.final 常量
   3.private 方法
对象类型的转换:
   //上转型对象(将子类对象当作父类类型来使用)
   /**
    * 此时不能通过父类对象去调用子类特有的方法,不能操作子类新增加的成员变量(较子类失去一些属性和功能)
    * 上转型对象可以操纵父类原有的属性和功能,无论这些方法是否被重写
    * 上转型对象调用方法时,就是调用子类继承和重写过的方法
    */
abstract class Animal{
    
}

class Cat extends Animal{
    
}

class Dog extends Animal{
    
}

Animal an1 = new Dog();		//将Dog类当作Animal类来使用
Animal an2 = new Cat();		//将Cat类当作Animal类来使用

//下转型对象
//将本质为Dog类型的an1对象由Animal类型向下转型为Dog,就可以使用Dog类型的所有属性和方法

关键字instanceof

语法格式:
    对象(或者对象引用变量) instanceof(或接口)
    
    Animal an1 = new Dog();
    if(an1 instanceof Cat){		//判断an1本质类型
        Cat cat = (Cat)an1;
    }
	else{
        System.out.println("该类型的对象不是Cat类型!"); 
         
    }

6.异常


Throwable是所有异常的超类
Exception 受检查异常,非运行时异常
RuntimeException  非受检查异常,运行时异常
   
异常处理机制
try{	//try监控区域
    
}catch{		//捕获异常
    
}finally{	//处理善后工作,程序必定会执行
    
}

如果要捕获多个异常,从小范围到大范围
    
快捷生成 Ctrl + Alt + T
    
//假设在这方法中,处理不了这个异常,方法上抛出异常
public void test(int a,int b) throws ArithmeticException{
    if(b==0){
        throw new ArithmeticException();//主动抛出的异常,一般在方法中使用
    }
}

7. 泛型

泛型提供了编译时类型安全监测机制,允许我们在编译时检测到非法的类型数据结构

Java进阶


1.集合和对象数组


1.1 集合

Collection接口(元素是孤立存在的,单列集合)
    List接口和Set接口的父接口
    定义的是所有单列集合中共性的方法
    所有单列集合都可以使用共性的方法
    没有带索引的方法
    

List 接口
    1.有序的集合(存储和取出元素顺序相同)
    2.允许存储重复的元素
    3.有索引,可以使用普通的for循环遍历
    实现类:
    	1.Vector集合
    	2.ArrayList集合
    	3.LinkedList集合
    
Set接口
    1. 不允许存储重复元素
    2.没有索引(不能使用普通的for循环遍历)
    实现类:
    	1.TreeSet集合
    	2.HashSet集合
    
    
Map接口(元素是成对存在的,每个元素由键与值两部分组成,通过键可以找到对应的值)
    Map中的集合不能包含重复的键,值可以重复,每个键只能对应一个值
    

1.2 对象数组

定义一个数组来存储3个Person对象
    缺点:一旦创建,程序运行时期长度不可以发生改变

public class Person{
    private String name;
    
    public String getName(){
       return name;
    }
}

public static void main(String[] args){
    //首先创建一个长度为3的数组,里面用来存放Person类型的对象
    Person[] array =  new Person[3];
    
    Person one = new Person();
    Person two = new Person();
    Person three = new Person();
    
    array[0] = one;
    array[1] = two;
    array[2] = three;
    
    System.out.println(array[1].getName());
}

1.3 ArrayList集合

List接口的实现类
ArrayList集合的长度是可以随意变化的
    底层是数组,查询快,增删慢
对于ArrayList来说,有一个<E>代表泛型
    泛型,也就是装在集合当中的所有元素,全都是统一的什么类型
对于ArrayList集合来说,直接打印得到的不是地址值,而是内容,如果内容是空,得到的是空的中括号,[]
    
ArrayList<String> list = new ArryList<>();


/*
	ArrayList当中的常用方法:
	
	public boolean add(E e),向集合中添加元素,参数的类型和泛型一致   //返回值代表添加是否成功
	public E get(int index),从集合中获取元素,参数是索引编号(即类似数组下标),返回值为对应为对应位置的元素
	public E remove(int index),从集合中删除元素,参数是索引编号(即类似数组下标),返回值为被删除掉的元素
	public int size(),获取集合的尺寸长度,返回值是集合中包含的元素个数
*/

如果希望想集合ArrayList当中存储基本类型,必须使用基本类型对应的包装类
    自动装箱:基本类型 -> 包装类型
    自动拆箱:包装类型 -> 基本类型

1.4 LinkedList

List接口的实现类
底层由链表进行实现的
里面包括了大量操作首尾元素的方法
使用LinkedList集合特有的方法,不能使用多态

public boolean addFrist(){

}
public boolean addLast(){

}
public E getFirst(){
    
}
public E getLast(){
    
}
public E removeFirst(){
    
}
public E removeLast(){
    
}

1.5 Set接口

1.不允许存储重复的元素
2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
    
HashSet特点
    1.不允许存储重复的元素
	2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历,可使用迭代器和增强for循环
    3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致
    4.底层是一个哈希表结构(查询速度非常快)
    
    ArrayList<String> list = new ArryList<>();
	Iterator<String> it = list.iterator();

1.6 HashSet集合

哈希值:是一个十进制的整数,由系统随机给出,就是对象的地址,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址
    
HashSet集合存储数据的结构(哈希表)		//无序,不允许重复
    jdk1.8之前哈希表=数组+链表
    jdk1.8之后:
    	哈希表=数组+链表
    	哈希表=数组+红黑树(提高查询效率)
    
    LinkedHashSet					//有序,不允许重复

1.7 Collections类

集合工具类,用来对集合进行操作    
    public static <T> boolean addAll(Collection<T> c, T...elements);往集合中添加一些元素    
    public static void shuffle(List<?> list);                       打乱集合顺序    
    public static <T> vodi sort(List<T> list);                      将集合中元素按照默认规则进行排序(被排序的集合里边存储的元素,必须实现实现Comparable接口,重写了compareTo方法作为排序的规则)

1.8 红黑树(平衡排序树)

排序树:左子树小,右子树大
平衡树:左孩子和右孩子相等

约束:
    1.节点可以是红色或者黑色的
    2.根节点是黑色的
    3.叶子结点是黑色的
    4.每个红色的节点的子节点都是黑色的
    5.任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

2. Java多线程

程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
进程:执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位

通常一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位

main()称之为主线程,为系统的入口,用于执行整个程序
    线程开启不一定立即执行,

tips:

很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所有就有同时执行的错觉

普通方法调用和多线程

2.1 创建线程的方式

三种创建方式
    Thread class		 继承Thread类(重点)
    Runnable 接口		    实现Runnable接口(重点)
    Callable 接口		    实现Callable(了解)
        
继承Thread类
        子类继承Thread类具备的多线程能力
        启动线程:子类对象.start()
        不建议使用:避免oop单继承的局限性
        
实行Runnable接口
        实现接口Runnable具有多线程能力
        启动线程:new Thread(实现类对象).start()
        推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
        
多个线程共享一个资源,只需创建一个该资源对象

2.2 继承Thread类创建线程

创建线程方式一:继承Tread类,重写run()方法,调用start开启线程
    
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run 方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---"+i);
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        
        //创建一个线程对象
        TestThread1 testThread1 = new TestThread1();
        
        //调用start方法
        teatThread1.start();
        
        
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习多线程---"+i);
        }
    }
}

两条线程同时执行,交替执行的

2.3 网图的下载

//首先导入jar包

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TextThread extends Thread{
    private String name;
    private String url;

    public TextThread(String url,String name){
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WecDownloader wecDownloader = new WecDownloader();
        wecDownloader.downloader(url, name);
    }

    public static void main(String[] args) {

        TextThread textThread = new TextThread("https://tse3-mm.cn.bing.net/th/id/OIP-C.s4LzOcaohmeW3Qkjwy153AHaFP?w=266&h=188&c=7&r=0&o=5&dpr=1.5&pid=1.7", "1.jpg");
        textThread.start();
    }
}


class WecDownloader{
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch (IOException e){
            System.out.println("IO异常");
        }

    }
}

2.4 实现Runnable接口(常用)

适合于无返回值的方法
    避免了Java单继承带来的局限性,通过Runnable接口实现多线程
    
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TextThread implements Runnable{
    private String name;
    private String url;

    public TextThread(String url,String name){
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WecDownloader wecDownloader = new WecDownloader();
        wecDownloader.downloader(url, name);
    }

    public static void main(String[] args) {

        TextThread textThread = new TextThread("https://tse3-mm.cn.bing.net/th/id/OIP-C.s4LzOcaohmeW3Qkjwy153AHaFP?w=266&h=188&c=7&r=0&o=5&dpr=1.5&pid=1.7", "1.jpg");
        new Thread(textThread).start();
    }
}

//下载器
class WecDownloader{
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch (IOException e){
            System.out.println("IO异常");
        }

    }
}

2.5 实现Callable接口

适合于有返回值的方法,并抛出异常

    

//借助FutureTask执行

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//定义一个实现Callable接口的实现类
public class TextCallable implements Callable<Boolean> {
    private String name;
    private String url;

    public TextCallable(String url, String name){
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception{
        WecDownloader wecDownloader = new WecDownloader();
        wecDownloader.downloader(url, name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建Callable接口的实现类对象
        TextCallable textCallable = new TextCallable("https://tse3-mm.cn.bing.net/th/id/OIP-C.s4LzOcaohmeW3Qkjwy153AHaFP?w=266&h=188&c=7&r=0&o=5&dpr=1.5&pid=1.7", "1.jpg");

        //使用FutureTask封装Callable接口
        FutureTask<Boolean> ft1 = new FutureTask<>(textCallable);

        //使用Thread(Runnable target,String name)构造方法创建线程对象
        Thread thread1 = new Thread(ft1,"thread1");

        //调用线程对象的start方法启动线程
        thread1.start();

        //可以通过FutureTask对象的方法管理返回值
        System.out.println("thread1返回结果:"+ft1.get());
    }
}


class WecDownloader{
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch (IOException e){
            System.out.println("IO异常");
        }

    }
}




//借助线程池来执行
    
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//定义一个实现Callable接口的实现类
public class TextCallable implements Callable<Boolean> {
    private String name;
    private String url;

    public TextCallable(String url, String name){
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Excepton{
        WecDownloader wecDownloader = new WecDownloader();
        wecDownloader.downloader(url, name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        TextCallable textCallable = new TextCallable("https://tse3-mm.cn.bing.net/th/id/OIP-C.s4LzOcaohmeW3Qkjwy153AHaFP?w=266&h=188&c=7&r=0&o=5&dpr=1.5&pid=1.7", "1.jpg");

        //创建执行服务
        ExecutorService service = Executors.newFixedThreadPool(1);

        //提交执行
        Future<Boolean> r1 = service.submit(textCallable);

        //获取结果
        boolean rs1 = r1.get();

        //关闭服务
        service.shutdown();
    }
}


class WecDownloader{
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch (IOException e){
            System.out.println("IO异常");
        }

    }
}

2.6 Lambda表达式

避免匿名内部类定义过多
可以让代码看起来更加简洁
去掉了一堆没有意义的代码,只留下了核心的逻辑
    
函数接口的定义:
    任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
    对于函数式接口,我们可以通过lambda表达式来创建接口的对象
//定义一个接口
public interface MyInterface {
    void method();
}

//接口实现类
public class MyInterfaceImpl implements MyInterface{
    @Override
    public void method() {
        System.out.println("匿名内部类实现了方法");
    }
    
}



//匿名内部类
MyInterface myinterface = new MyInterface(){
  	@Override
    public void method() {
        System.out.println("匿名内部类实现了方法");
    }
};



//lambda表达式简化
MyInterface myinterface = ()->{
        System.out.println("匿名内部类实现了方法");
    };


//有参数的lambda表达式
MyInterface myinterface = (int a)->{
        System.out.println("匿名内部类实现了方法"+a);
    };

//简化1:参数类型
MyInterface myinterface = (a)->{
        System.out.println("匿名内部类实现了方法"+a);
    };

//简化2:简化括号
MyInterface myinterface = a->{
        System.out.println("匿名内部类实现了方法"+a);
    };

//简化2:去掉花括号(前提是花括号里面只有一行代码,一般不常用)
MyInterface myinterface = a->System.out.println("匿名内部类实现了方法"+a);

总结:
    lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,用代码块进行包裹
    前提是接口为函数式接口
    多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

2.7 线程状态

五大状态之间的关系图

2.7.1 线程停止(stop)
public class TestStop implements Runnable{
    //线程中定义县城提使用的标识
    private boolean flag = true;

    @Override
    public void run() {
        //线程体使用该标识
        while (flag){
            System.out.println("run... Thread");
        }
    }
    
    //对外提供方法改变标识
    public void stop(){
        this.flag = flag;
    }
}
2.7.2 线程休眠(sleep)
sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间达到后线程进入就绪状态
sleep可以模拟网络延时,倒计时等
每一个对象都有一个锁,sleep不会释放锁
2.7.3 线程礼让(yield)
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功,看cpu心情
2.7.4 线程强制执行(join)
join合并线程,待此线程执行完成后,再执行其他线性程,其他线程堵塞
可以想象成c
2.7.5 观测线程状态
Thread.State state = thread.getState();
2.7.6 线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
    
优先级的设定在start()调度之前
线程优先级用数字表示,范围从1~10
    Thread.MIN_PRIORITY = 1;
	Thread.MAX_PRIORITY = 10;
	Thread.NORM_PRIORITY = 5;

优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度
    
//改变优先级
    对象名.setPriority(int xxx)
//获取优先级
    对象名.getPriority()
2.7.7 守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
such:后台记录操作日志,监控内存,垃圾回收等待

//上帝
class God implements Runnable{
    @Override
    public void run(){
        
    }
}

//你
class You implements Runnable{
    @Override
    public void run(){
        
    }
}

public static void main(String argc){
    God god = new God();
    You you = new You();
    
    Thread thread = new Thread(god);
    thread.setDaemon(true);		//默认是false表示是用户线程,正常的线程都是用户线程
    
    thread.start();		//上帝守护线程启动
    
    new Thread(you).start();		//你 用户线程启动
}

2.8 线程同步

并发:同一个对象被多个线程同时操作
    
处理多线程问题是,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
    
为了保证数据在方法中被访问的正确性,在访问时加入锁机制 synchronized(同步的),当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可。但存在以下问题
    1.一个线程持有锁会导致其他所有需要此锁的线程挂起
    2.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    3.如果一个优先级高的线程等待一个优先级低的线程会释放锁,会导致优先级倒置,引起性能问题

2.9 同步方法和同步代码块

每个对象对应一把锁
方法里面需要修改的内容才需要锁,锁的太多,浪费资源
    
锁的对象就是变化的量,需要增删改的对象
//同步方法
public synchronized void run(){
    
}

//同步代码快
synchronized(Obj){
    
}
Obj称为同步监视器,推荐使用共享资源作为同步监视器
    
同步监视器的执行过程
    1.第一个线程访问,锁定同步监视器,执行其中的代码
    2.第二个线程访问,发现同步监视器被锁定,无法访问
    3.第一个线程访问完毕,解锁同步监视器
    4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问

2.10 死锁

产生死锁的四个必要条件:
    1.互斥条件:一个资源每次只能被一个进程使用
    2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
    4.循环等待条件:若干进程之间形成的一种头尾相接的循环等待资源关系

2.11 Lock(锁)

synchronized和lock的对比
    Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
    Lock只有代码块锁,synchronized有代码块锁和方法锁
    使用Lock锁,JVM将花费更少的时间来调度线程,性能更好,并有更好的扩展性(提供更多的子类)
    优先使用顺序:
    Lock > 同步代码块(已经进入了方法体,分配了相应的资源) > 同步方法(在方法体之外)

ReentrantLock(可重入锁)
    
class A{
    private final ReentrantLock lock = new ReentrantLock();
    public void m(){
        lock.lock();			//加锁
        try{
            //保证线程安全的代码
        }finally{
            lock.unlock();		//解锁
            //如果同步代码有异常,要将unlock()写入finally语句块
        }
    }
}

2.12 线程通信

Java提供了几个方法来解决线程之间的通信问题(Object类中的方法,只能在同步方法或者同步代码块中使用)
    wait()			表示线程会一直等待,直到其他线程通知,与sleep不同,会释放锁
    wait(long timeout)		指定等待的毫秒数
    notify()		唤醒一个处于等待状态的线程
    notifyAll()		唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

并发协作模型”生产者/消费者模式”

管程法(l)

生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
缓冲区:消费者不能直接使用生产者的数据,它们之间有个"缓冲区"
    
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据 
public class Text {
    public static void main(String[] args) {
        SyContainer container = new SyContainer();

        new Productor(container).start();
        new Customer(container).start();
    }

}

//生产者
class Productor extends Thread{
    SyContainer container;

    public Productor(SyContainer container){
        this.container = container;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}

//消费者
class Customer extends Thread{
    SyContainer container;

    public Customer(SyContainer container){
        this.container = container;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }
}

class Chicken{
    int id;
    public Chicken(int id){
        this.id = id;
    }
}

//缓冲区
class SyContainer{
    Chicken[] chickens = new Chicken[10];

    int count = 0;
    //生产者放入产品
    public synchronized void push(Chicken chicken){
        if(count == chickens.length) {
            //等待消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            chickens[count] = chicken;
            count++;
            this.notifyAll();

    }
    //消费者使用产品
    public synchronized Chicken pop(){
        if(count == 0){
            //通知生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count --;
        Chicken chicken = chickens[count];
        this.notifyAll();
        return chicken;
    }

}
信号灯法
    

3. Java I/O流


3.1 输入流和输出流

四大输入输出流(抽象类,使用时使用其子类):  
	InputStream(字节输入流):
 	    所有字节输入流的超类,提供从一个字节输入流读取数据的基本方法
	OuputStream(字节输出流):
    	所有字节输出流的超类,提供向一个字节输出流写入数据的基本方法
	Reader(字符输入流):
    	所有字符输入流的超类,提供从一个字符输入流读取数据的基本方法
	Writer(字符输出流):
   		所有字符输出流的超类,提供向一个字符输出流写入数据的基本方法