Java一阶段复习

java跨平台:

Java源代码——》Java字节码——》对应平台的运行

编译期:.Java文件经过编译变为.class文件

运行期:JVM加载.class字节码文件并运行

跨平台:不同操作系统安装对应的JVM,统一代码可以运行在不同的OS之上

JDK:

JDK是Java语言的软件开发工具包,包含了

        1.Java开发工具(jdk、bin)

        2.基础开发库(jdk,jre,lib,rt.jar)

        3.基础开发库的源代码(jdk,src.zip)

JDK——Java开发工具包,JRE=开发工具

开发java程序最小环境为JDK,所以JDK是Java的核心语言

运行JAVA的最小环境为JRE

JVM负责加载.class并运行.class文件

JVM(JAVA虚拟机)将JAVA代码转换为对应的操作系统可以理解的指令,不同的操作系统有不同虚拟机与之对应,统一代码交给虚拟机后,虚拟机再转化给操作系统

JDK JRE JVM关系

变量:

可以改变的量被称为变量,不会改变的量被称为常量

在Java中变量在使用前必须被声明,一般通过:变量类型 变量名=变量值;来描述一个变量

局部变量:

定义在方法或局部代码块里,方法运行完内存就释放了

成员变量:

定义在类里,方法外

不被初始化,也会被自动赋予默认值

类结束,变量才会被释放

数组:

动态初始化: int[] a = new int[5];

静态初始化:int[] b = new int[]{1,2,3,4,5}   int[] c={1,2,3,4,5}

数组工具Arrays.toString(数组) 把数组里的数据,用都好连接成一个字符串[值1,值2]

Arrays.sout(数组)对数组进行排序

Arrays.copyOf(数组,新的长度)
把数组赋值成一个指定长度的新数组
新数组的长度 大于 原数组, 相当于复制,并增加位置
新数组的长度 小于 原数组, 相当于截取一部分数据

面向对象

面向对象是一种思想。面向:面对,注重。对象:目标

面向对象,就是以对象为目标的一种编程实现

面向对象的三大特征:封装,继承,多态

封装:提高安全性,提高重用性

preivate关键字。是一个权限修饰符,用来修饰成员变量或者方法,被私有化的成员只能在本类中访问。

可以通过set,get来设置读取私有资源,通过重写toString来显示数据

构造方法:

构造方法是一种特殊的方法,它是一个与类同名且没有返回值类型的方法
构造方法的主要功能就是完成对象创建或者初始化
当类创建对象(实例化)时,就会自动调用构造方法
构造方法与普通方法一样也可以重载.

this与super

this表示本类对象的一个应用对象,引用本类
super表示父类对象的一个应用对象,引用父类
注意:this和super在构造方法里,出现的调用位置必须是第一层,注意,不能相互调用,会死循环
super是建立了继承关系以后,子类如果想用父类的功能,可以通过super调用
如果重写后还想用父类的功能,需要使用super来调用
super在调用父类构造方法时,必须出现在子类构造方法的第一条语句,而且如果父类中没有提供无参构造,子类可以通过super来调用父类其他的含参构造

继承:继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并扩展新的能力.

使用extends关键字表示继承关系

Java只支持单继承

继承可以多代

父类的私有成员也会被继承,但是无法使用父类的私有资源

 重载Overload 与重写Override的区别

重载:方法名相同,提交的方法参数类型不同,不等

重写,方法名相同,提交参数类型相同,功能更改。重写方法的修饰符: 子类权限 >= 父类的权限

重载的意义: 是为了方便外界对方法进行调用,什么样的参数程序都可以找到对应的方法来执行,体现的是程序的灵活性

重写的意义:是在不修改源码的前提下,进行功能的修改和拓展(OCP原则:面向修改关闭,面向拓展开放)

static

修饰成员或者方法

随着加载而加载,优于对象加载,并且初始化

只加载一次,不会重复加载

全局唯一

可以使用类名调用

静态只能调用静态,非静态可以随便调用

不能和this与super一起使用,因为有static时可能还没有对象

final

是常量的定义标识符

无法被子类改变

不能被继承,重写,修改

多态

使用父类的对象,存储子类的目标,这种行为成为多态

异常

异常的总父类(祖宗)Throwable

        --Error错误,程序无法处理(大爷)

        --Exception异常,可以被修复(二爷)

在这里插入图片描述

异常的处理方式:

        当程序中遇到了异常,通常有两种处理方式:捕获或者向上抛出,当一个方法抛出异常,调用位置可以不做处理继续向上抛出,也可以捕获处理异常
捕获方式:try{捕获的异常}catch(异常类型 异常名){处理方案}flain{最终手段,不管有没有异常都会执行}

抛出方式:

对于不想现在处理或者处理不了的异常可以选择向上抛出,在可能会会发生异常的方法上添加代码
例如:void method1 throws Exception1,Exception2,Exception3{ }

throws 与 throw 的区别:
throws
用在方法声明处,其后跟着的是异常类的名字
表示此方法会抛出异常,需要由本方法的调用者来处理这些异常
但是注意:这只是一种可能性,异常不一定会发生

throw
用在方法的内部,其后跟着的是异常对象的名字
表示此处抛出异常,由方法体内的语句处理
注意:执行throw一定抛出了某种异常

抽象

Java中可以定义被abstract关键字修饰的方法,这种方法只有声明,没有方法体,叫做抽象方法.
Java中可以定义被abstract关键字修饰的类,被abstract关键字修饰的类叫做抽象类

  1. 如果一个类含有抽象方法,那么它一定是抽象类
  2. 抽象类中的方法实现交给子类来完成

abstract可以修饰抽象方法或者类

抽象类可以没有抽象方法

如果类中有一个抽象方法,那么一定是一个抽象类

子类继承后,要么还是抽象类,要么需要重写具体抽象方法

抽象类本身不可以实例化

抽象类不可以与以下关键词一起使用

1.private:被私有化后,子类无法重写,与abstract相违背。
2.static:静态优先于对象存在,存在加载顺序问题。
3.final:被final修饰后,无法重写,与abstract相违背。

接口:

接口( Interface )在Java中也是一种抽象类型,接口中的内容是抽象形成的需要实现的功能,接口更像是一种规则和一套标准.

通过interface定义几口

通过implements让子类实现接口

接口中的方法都是抽象方法

接口突破了Java的单继承的局限性

接口与类之间可以多实现,接口与接口之间可以多继承

接口提高了扩展性,降低了耦合性

接口之中没有构造方法,创建现实类的对象时默认的super(),时调用的默认Object的无参构造

接口之中没有成员变量,都是常量,所以你定义变量没有写修饰符时,默认加上public static final

接口之中的方法默认为抽象的,不屑abstract会默认补齐。例如:public abstract void save

1. 类与类的关系
继承关系,只支持单继承
比如,A是子类 B是父类,A具备B所有的功能(除了父类的私有资源和构造方法)
子类如果要修改原有功能,需要重写(方法签名与父类一致 + 权限修饰符>=父类修饰符)

2. 类和接口的关系
实现关系.可以单实现,也可以多实现
class A implements B,C{}
其中A是实现类,B和C是接口,A拥有BC接口的所有功能,只是需要进行方法的重写,否则A就是抽象类

3. 接口与接口的关系
是继承关系,可以单继承,也可以多继承
interface A extends B,C{}
其中ABC都是接口,A是子接口,具有BC接口的所有功能(抽象方法)
class X implements A{}
X实现类需要重写ABC接口的所有方法,否则就是抽象类
class A extends B implements C,D{}
其中A是实现类,也是B的子类,同时拥有CD接口的所有功能
这时A需要重写CD接口里的所有抽象方法

4. 抽象类与接口的区别
抽象类是一个特殊的类,特殊在,抽象类中可以包含没有方法体的方法(抽象方法)
接口可以理解成一个特殊的抽象类,特殊在,接口里的都是抽象方法,没有普通方法
接口会为方法自动拼接public abstract,还会为变量自动拼接public final static
抽象类可以有构造方法–用来给子类创建对象,接口中没有构造方法
抽象类和接口都不能实例化(创建对象)
接口可继承接口,并可多继承接口,但抽象类只能单继承
抽象方法只能声明,不能实现,接口是设计的结果 ,抽象类是重构的结果
 

API

API应用程序接口,是一些预定的函数,一种通用功能集,是一种接口标准

Object是一个顶级父类(祖宗),无序手动导包

toString()返回对象的字符串表示

hashcode()返回对象的哈希码值

String  String是一个封装Char[]数组,字符串不可变,是一个常量

int hashCode()返回此字符串的哈希码值

boolean equals(Object anObject)将此字符串与指定的对象作比较,比较的是重写后的具体内容

String to Sting返回此对象本身(他已经是一个字符串!)

int length()返回此字符串的长度

String toUpperCase()转大写

Sting toLowerCase()转小写

boolean startsWith(String prefix)是否已指定元素开头

boolean endWith(String suffix)是否以指定元素结尾

char charAt(int index)返回指定位置的字符

int indexOf(int ch)第一次出现该字符的索引

int lastindexOf(int ch)最后一次出现的索引

String concat(String str)将指定字符串连接,产生新的字符串,不影响原串

String[] split(String regex)根据指定字符分割字符串

String trim()返回去除首尾空格的字符串

byte[] getBytes()把字符串储存到一个新的byte数组中

String substring(int beginIndex)返回一个新字串,从指定下标开始,包含指定下标

String uubstring(int beginIndex,int endIndex)返回一个新字串,从指定开始下标开始,到结束下标结束,包含开始,不包含结束下标

static String valueOf(int i)把int转换成String

StringBuilder/StringBuffer

封装了char[]数组

是可变的字符序列

提供了一组可以对字符内容修改的方法

用append()代替字符串做字符串链接“+”

内部默认初始容量为16:Super(str.length()+16)

如果大于16会进行阔若那个变成原来的2倍+2,

StringBuffer1.0出道线程安全(有synchronized锁,安全效率低),StringBuilder1.5出道线程不安全(没有synchronized锁,不安全效率高)

执行效率上StringBuilder>StringBuffer>String

==如果两个变量时数值变量,比较数值,如果是引用类型比较内存地址

equals根据具体实现方法而定

正则表达式

判断输入的文字是否符合规则要求

在这里插入图片描述

 在这里插入图片描述

自动装箱:把 基本类型 包装成对应的 包装类型 的过程
Integer a = 5;//a是引用类型,引用了包装对象的地址。
编译器会完成对象的自动装箱:Integer a = Integer.valueOf(5);

自动拆箱:从包装类型的值,自动变成 基本类型的值
int i = a;//a现在是包装类型,没法给变量赋值,需要把5取出来。
编译器会完成自动拆箱:int i = a.intValue();

BigDecimal

BigDecimal:常用来解决精确的浮点数运算不精确的问题

BigDecimal(double val)
将double转换为BigDecimal,后者是double的二进制浮点值十进制表示形式,有坑!
方式二 :
BigDecimal(String val)
将String类型字符串的形式转换为BigDecimal

Add(BigDecimal bd);加法

Subtract(BigDecimal bd);减法

Multiply(BigDecimal bd);乘法

Divide(BigDecimal bd):除法,除不尽会有异常

Divide(BigDecimal bd,int 保留位数,舍入方式);除法除不尽使用

setScale(Int 保留位数,舍入方式);保留小数位数

pow(int n);求数据的几次幂

ROUND_HALF_UP四舍五入

POUND_HALF_DOWN五舍六入

ROUND_HALF_EVEN公平舍入(银行使用)

ROUND_UP有就进一

ROUND_DOWN多少都舍去

ROUND_CEILING(天花板)向上取整,取实际的最大值

ROUND_FLOOR(地板)向下取整,取实际的最小值

 IO流

单向流动,输入流——in,输出流——out,只能从头到尾读写一次在这里插入图片描述

 IO流的继承结构

字节流:针对二进制文件

字符流:针对文本文件,容易乱吗,要制定编码集为UTF-8

File

字节流:针对二进制文件

InputStream

FileInputStream

BufferedInputStream

ObjectInputStream

OutputStream

FileOutputStream

BufferedOutputStream

ObjectOutputStream

字符流:针对文本文件

Reader

FileReader

BufferedReader

InputStreamReader

Writer

FileWriter

BufferedWriter

OutputStreamWriter

PrintWriter一行行写出

 File文件类:

创建对象

new File(”d/abc/a.txt“);

new File("d:abc,"a.txt");

常用方法

lenght() 查询文件的字节量

exists() 查询文件是否存在,存在返回true

isFile()是否是文件,是返回true

isDirectory()是否是文件夹,是返回true

getName()获取文件名字

getParent()获取父文件夹的路径

getAbsolutePath()获取文件的完整路径

createNewFile()新建文件,文件夹不存在会异常,已存在返回false

makdlr()新建单层不存在的文件\m

makdirs()新建多层不存在的文件夹\a\b\c

delete()删除文件,删除空文件夹

list()返回String[],包含文件名

listFiles()返回file[],包含文件对象

字节流读取

字节流是由字节组成的,字符流是由字符组成的

Java的字符由两个字节组成,字节流是基本流,处理二进制数据

InputStream抽象类

是一个抽象父类,不可使用

常用方法

abstract int read()冲输入流中读取下一个字节

int reda(byte[] b)冲输入流中读取一定量的数据,并将其存入缓冲区数组b中

int read(byte[] b,int off,int len)将输入流中最多len个数据字节读入byte数组,off表示存的偏移量

void close()关闭流并释放与流相关的所有系统资源

FileInputStream(普通文件字节传输流)

创建对象 FileInputStream(File file)——直接传文件对象

通过打开一个实际文件的链接创建一个FileInputStream,该文件通过文件系统的路径名指定

BufferedInputStream(高效文件字节传输流)

为输入流添加了一些功能,会一创建一个内部缓冲区(默认8k大小),在读取或跳过流中的字节时,可以根据需要从包含的输入流中再次填充该内部缓冲区,一次填充多个字节

创建对象 BufferedInput Stream(InputStream in)  //BufferedInput Stream(FileInputStream in)

字符流读取

Reader抽象类

常用方法:

int read()读取单个字符

int read(char[] cbuf)将字符读入数组

abstract int read(char[] cbuf,int off,int len)将字符读入数组的某一部分,len个数据字节读入byte数组,off表示存的偏移量

int read(CharBuffer target)试图将字符读入指定的字符缓冲区

abstract void close()关闭该流并释放与之相关联的所有资源

FileReader

创建对象

FileReader(String fileName)在给定从中读取数据的文件名的情况下创建一个新的FileReader

FileReader(File file)在给定从中读取数据的File的情况下创建一个新的FileReader

BufferedReader

创建对象

BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流

字节流写出

OutoutStream抽象类

常用方法

Void close()关闭流并释放相关资源

void flush()刷新此输出流并强制写出所有的缓存的输出字节(防止数据漏写,丢失)

void writer(byte[] b)将b.length个字节从指定的byte数组写入此输出流

void writer(byte[] b,int off,int len)将指定byte数组中从偏移量off开始的len个字节写出输出流

Abstract void write(int b)将指定的字节写入此输出流

FileOutputStream

在文件中写出数据

常用方法

FileOutputStream(String name)创建一个向指定name文件写出数据的文件输出流

FileOutputStream(File file)创建一个指定文件file写出数据的文件输出流

FileOutputStream(File file,boolean append)true默认追加,false才是覆盖

象一个指定文件file写入数据的文件输出流,后面的判断判断是否覆盖原文件

BufferedOutputStream 高效字节输出流

构造方法

BufferedOutputStream(OutputStream )        //BufferedOutputStream (FileOutputStream )

创建一个新的缓冲输出流,将数据写入指定的底层输出流

字符流写出

Writer抽象父类

常用方法

abstract void close()关闭流并释放相关资源

void write(char[] cbuf)写入字符数组

void write(int c)写入单个字符

void write(String str)写入字符串

void writer(Sting str,int off,int len)写入字符串的某一部分,len个数据字节读出Sting ,off表示存的偏移量

abstract void write(char[] cbuf,int off,int len)写入字符数组的某一部分,,len个数据字节读出Sting ,off表示存的偏移量

FileWriter

构造方法

FileWriter(String filename)根据给定的名字创建一个FileWriter的写出对象

FileWriter(String filename,Boolean append)默认追加,不覆盖

根据给定的文件名构造FileWritrt,booleran指示是否追加写入

BufferedWriter 高效输出流

构造方法

BufferedWriter(Writer out)        //BufferedWriter(FileWriter)

创建一个使用默认大小的输出缓冲区的缓冲字符输出流

序列化与反序列化

序列化ObjectOutputStream是将对象的状态信息转换为可以储存或传输的形式的过程,将对象当前状态读取到临时或持久性的储存区,以后可以利用反序列化ObjectInputStream重新创建该对象

特点:

必须实现Serializable接口,来启动序列化功能

不需要序列化的数据可以修饰为static

每个被序列化的文件都有唯一的id,如果没有,会自动生成

序列化与反序列化的版本应当一致

常用于服务器之间的数据传输,序列化成文件,反序列化读取数据

常使用陶杰字流在主机间传递对象

不需要序列化的数据可以被修饰成transient(临时的),只在程序运行期间内存中存在,不会被序列化持久保持

序列化ObjectOutputStream

将对象的状态信息转换为可以储存或传输的形式的过程,将对象当前状态读取到临时或持久性的储存区

构造方法

ObjectOutputStream(OutputStream out)        //ObjectOutputStream(FileOutputStream)

将指定的对象写入 ObjectOutputStream

反序列化ObjectInputStream

构造方法

ObjectOutputStream(inputStream out)        //ObjectinputStream(FileinputStream)

从 ObjectInputStream 读取对象 

为什么反序列化版本号需要与序列化版本号一致?

JVM会拿着反序列化流中的serialVersionUID与序列化时相应的实体类中的serialVersionUID来比较,如果不一致,就无法正常反序列化,出现序列化版本不一致的异常InvalidClassException。

而且我们在定义需要序列化的实体类时,如果没有手动添加UID,
Java序列化机制会根据编译的class自动生成一个,那么只有同一次编译生成的class才是一样的UID。

如果我们手动添加了UID,只要这个值不修改,就可以不论编译次数,进行序列化和反序列化操作。

泛型

通过泛型的语法定义<>,来约束集合中元素的类型,编译器可以在编译期根据泛型约束提供一定的类型安全检查,这样可以避免程序运行时才暴露BUG,代码的通用性也会更强在这里插入图片描述

  Collection接口在这里插入图片描述

 Collection接口

List 接口【数据有下标,有序,可重复】

ArrayList子类

LinkedList子类

Set 接口【数据无下标,无序,不可重复】

HashSet子类

Map 接口【键值对的方式存数据】

HashMap子类

 add(E e)为集合添加数据

addAll(Collection c)把小集合添加到大集合中

remove(Object o)移除对象

isEmpty()是否为空?

size()集合元素个数

contains(Obj o)是否包含某元素

toArray() 转化为数组对象

iterator()获取迭代器,用于遍历对象

 List

有序,有下表,可重复

get(int i)获取指定元素

 set(int i,obj o)在指定位置修改元素

add(int i,E e)在指定位置插入元素

addAll(int i,collection<?> c)在指定位置插入列表

subList(int from,int end)输出指定区间段的列表,含头不含尾

listIterator()返回迭代器

listIterator(int i)返回指定元素的迭代器

ArrayList

以数组结构存放数据

初始容量10,扩容因子1.5

查询快,删减慢

LinkedList

底层结构为链表

查询慢(两头查询快),增删快

void addFirst(E e) 添加首元素
void addLast(E e) 添加首元素
E getFirst() 获取首元素
E getLast() 获取尾元素
E element() 获取首元素
E removeFirst() 删除首元素
E removeLast() 删除尾元素
boolean offer(E e) 添加尾元素
boolean offerFirst(E e) 添加首元素
boolean offerLast(E e) 添加尾元素
E peek() 获取首元素
E peekFirst() 获取首元素
E peekLast() 获取尾元素
E poll() 返回并移除头元素
E pollFirst() 返回并移除头元素
E pollLast() 返回并移除尾元素

Map

叫做哈希表、散列表. 常用于键值对结构的数据.其中键不能重复,值可以重复

  1. Map可以根据键来提取对应的值
  2. Map的键不允许重复,如果重复,对应的值会被覆盖
  3. Map存放的都是无序的数据
  4. Map的初始容量是16,默认的加载因子是0.75在这里插入图片描述

 常用方法

void clear() 从此映射中移除所有映射关系(可选操作)

boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true

boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true

Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图

boolean equals(Object o) 比较指定的对象与此映射是否相等

V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null

int hashCode() 返回此映射的哈希码值

boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true

Set keySet() 返回此映射中包含的键的 Set 视图

V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)

void putAll(Map<? extends K,? extends V> m)从指定映射中将所有映射关系复制到此映射中(可选操作)

V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)

int size() 返回此映射中的键-值映射关系数

Collection values() 返回此映射中包含的值的 Collection 视图

 在这里插入图片描述

 HashMap

HashMap底层是一个Entry[ ]数组,当存放数据时,会根据hash算法来计算数据的存放位置
算法:hash(key)%n , n就是数组的长度,其实也就是集合的容量
当计算的位置没有数据的时候,会直接存放数据
当计算的位置,有数据时,会发生hash冲突/hash碰撞,解决的办法就是采用链表的结构,在数组中指定位置处已有元素之后插入新的元素,也就是说数组中的元素都是最早加入的节点在这里插入图片描述

 set接口在这里插入图片描述

  1. 数据无序且数据不允许重复
  2. HashSet : 底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
  3. TreeSet : 底层是TreeMap,也是红黑树的形式,便于查找数据

初始空间16,扩容倍数*2+2 扩容因子0.75

进程

特点

独立性:是独立的储存在,有独立的资源和地址空间,会争抢公共资源,没有运行的情况下无法访问其他进程的地址空间

动态性:进程拥有自己的生命周期与不同的周期状态,是一个活动的指令集和

并发性:多个进程可以在单个cpu上并发执行,且互不影响并行与并发

线程

是操作系统OS进行运算调度的最小单位,一个进程可以开启多个线程,其中一个主线程调用本进程的其他线程

多线程扩展了多进程的概念,一个进程可以同时并发多个任务

各个线程的运行之间是不断切换的,使得各个程序从表面上看是同时进行的

进程与线程的关系

 线程的状态 (5+3)

线程的5种状态

创建:新建进程,计算机为该线程分配对应的资源空间,并将线程转到就绪队列

就绪:排队等待cpu分配时间片的状态

执行:cpu分配了对应的时间片,运行。时间片结束转会就绪状态,如果发生了阻塞,无法执行,将转为阻塞状态

阻塞(3):因为某种原因处于运行的线程,放弃了cpu的使用权,只有阻塞解决了之后才会转为就绪状态重新排队等待

  1. 其他阻塞:调用线程的sleep(),join()或发出IO请求(主动引发的阻塞)
  2. 同步阻塞:调用公共资源时,该资源被锁定(被其他程序占用),转为阻塞状态
  3. 等待阻塞:运行状态的线程执行wait()方法,进入等待阻塞

终止:线程已经执行完或者异常退出了,该线程生命周期结束

多线程的三种创建方法

继承Therad

编写简单,通过this获取当前线程

但无法再继承其他类

构造方法

Thread() 分配新的Thread对象
Thread(String name) 分配新的Thread对象
Thread(Runnable target) 分配新的Thread对象
Thread(Runnable target,String name) 分配新的Thread对象

普通方法

static Thread currentThread( )
返回对当前正在执行的线程对象的引用
long getId()
返回该线程的标识
String getName()
返回该线程的名称
void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则

调用该 Runnable 对象的 run 方法
static void sleep(long millions)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
void start()
使该线程开始执行:Java虚拟机调用该线程的run()

实现Runnable接口

如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口

重写run()方法,不可抛出异常,没有返回值

可以继承其他类,多线程共享一个target对象,适合多个相同线程处理同一份资源
编程稍复杂,通过Thread。current Thread()访问当前资源

//1.自定义多线程类,方式2 implements Runnable
class MyRunnable implements Runnable{

	//2.把业务放入run(),重写了Runnable接口里的
	@Override
	public void run() {
        //3.重写run()方法    
    }
}
public static void main(String[] args) {
		//4.创建线程对象
		MyRunnable target = new MyRunnable();
		//5.2 问题:怎么把接口的实现类和Thread类绑定
		Thread thread1 = new Thread(target);
		
		//5.1如何启动线程?
		thread1.start();
        //6.--以多线程编程的方式启动,需要创建多个线程对象并启动
		//    修改线程的名称--使用Thread类的含参构造
		Thread thread2 = new Thread(target,"杰克");
		Thread thread3 = new Thread(target,"露丝");
		thread2.start();
		thread3.start();
}

实现Callable

重写call()方法

  1. 可以抛出异常
  2. 有返回值,可以拿到reture对象,对任务进行操作

可以继承其他类,多线程共享一个target对象,适合多个相同线程处理同一份资源
编程稍复杂,通过Thread。current Thread()访问当前资源(与实现Runnable接口类似)

同步锁

如何判断可能出现安全问题,需要同步锁?

  1. 多线程程序中
  2. 有共享公用数据
  3. 多条语句操作共享数据

同步:一时刻只有一个线程可以调用,其他的要排队,效率低,安全性高

异步:多线程争抢资源,安全低,效率高

synchronized同步关键字

1.3.1 写法

synchronized (锁对象){
        需要同步的代码(也就是可能出现问题的操作共享数据的多条语句);
}

锁的范围不能太大,太大,干啥都得排队,也不能太小,太小,锁不住,还是会有安全隐患

线程池ExecutorService/Executors

ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理

创建线程池

newFixedThreadPool(int n Threads);最多n个线程的线程池

newCachedThreadPool();足够多的线程池,任务不必等待

newsingleThreadExecutor();只有一个线程的线程池

悲观锁

每次调用资源都会上锁。安全低效。

互斥锁synchronized 

采用synchronized修饰符实现的同步机制叫做互斥锁机制,每个对象都以一个所标记,只有拥有这个标记才可以访问这个资源,每个对象只有一个锁分配个一个线程

排他锁ReentrantLock 

同一时刻只有一个线程可以访问,”读/读“,”读/写“,”写/写“无法同时发生,降低了吞吐量,但是不存在数据竞争问题,如果”读读“操作可以以共享锁的方法进行,那会进一步提高性能

乐观锁

每次调用资源会尝试修改内存中的变量,如果修改失败表示发生冲突,应有相应的重试逻辑

读写锁ReentrantReadWriteLock 

主要用于读多写少的场景

读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的,只有一个线程可以同时持有

需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁会自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)从理论上讲,与互斥锁定相比,使用读-写锁允许的并发性增强将带来更大的性能提高。

两种单例设计模式

单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。

饿汉式

方法私有化,防止外部调用,尽可以通过公用的接口方法来使用,会创建一个唯一的单例对象 

public class Runtime {
	//1.创建静态的全局唯一的对象
private static Runtime currentRuntime = new Runtime();

//2.私有化构造方法,不让外部来调用
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    
	//3.通过自定义的静态方法获取实例
    public static Runtime getRuntime() {
        return currentRuntime;
    }
}

懒汉式

先不创建对象,使用时才创建,降低了内存的占用

package cn.tedu.single;
/*本类用于测试单例设计模式 2 - 懒汉式--面试重点!!!*/
/*总结:
* 关于单例设计模式的两种实现方式:
* 1.饿汉式 : 不管你用不用这个类的对象,都会直接先创建一个
* 2.懒汉式 : 先不给你创建这个类的对象,等你需要的时候再帮你创建--利用了延迟加载的思想
* 延迟加载的思想:是指不会在第一时间就把对象创建好来占用内存,而是什么时候用到,什么时候再去创建对象
* 3.线程安全问题 : 是指共享资源有线程并发的数据安全隐患,可以通过加锁的方式[同步代码块/同步方法]
* */
public class Singleton2 {
    public static void main(String[] args) {
        //6.创建对象进行测试
        MySingle2 s1 = MySingle2.getMySingle2();
        MySingle2 s2 = MySingle2.getMySingle2();
        System.out.println( s1 == s2 );//true,比较的是地址值,说明是同一个对象
        System.out.println( s1 );//cn.tedu.single.MySingle2@1b6d3586
        System.out.println( s2 );//cn.tedu.single.MySingle2@1b6d3586
    }
}
//0.创建单例程序
class MySingle2{
    //1.私有化构造方法,为了防止外部调用构造方法直接创建本类对象
    private MySingle2(){}
    //2.在类的内部创建好引用类型变量(延迟加载的思想)--注意私有化
    //5.2本处的引用类型变量也需要修饰成static的,因为静态只能调用静态,getMySingle2()是静态方法
    static private MySingle2 single2;
    //7.2.2同步代码块中使用的唯一的锁对象
    static Object o = new Object();
    /*问题:程序中有共享资源single2,并且有多条语句(3句)操作了共享资源
    * 此时single2共享资源在多线程环境下一定会存在多线程数据安全隐患
    * 解决方案1:同步代码块[加锁,范围是操作共享资源的所有代码]
    * 解决方案2:同步方法[如果方法中的所有代码都需要被同步,那么这个方法可以修饰成同步方法]
    * 注意事项:锁对象在静态方法中,不可以使用this,因为静态资源优先于对象加载
    * 锁对象可以使用外部创建好的唯一的锁对象o,但请注意,需要是静态的,静态只能调用静态
    * */
    //3.对外提供公共的全局访问点
    //5.1注意要使用static来修饰本公共方法,为了方便后续可以通过类名直接调用
    //7.1将方法修饰成同步方法
    synchronized static public MySingle2 getMySingle2(){
        //4.当用户调用此方法时,才说明用到这个对象了,那么我们就把这个对象返回
        /*注意:这里需要增加一个判断
        如果调用方法时single2的值为null,说明之前没有new过,保存的是默认值
        这时才需要new对象,如果single2的值不为null,直接return single2即可*/
        //7.2可以将操作共享资源的多条语句放入同步代码块之中
        synchronized (o) {
        //synchronized (this) {
            if (single2 == null) {
                single2 = new MySingle2();//没有对象时才创建对象,并赋值给single2
            }
            return single2;//有对象则直接返回single2
        }
    }
}

注解

同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。

是一种面对程序的扩展

分类

  • JDK自带注解
  • 元注解
  • 自定义注解

JDK的注解

  • @Override:采用表示来重写方法
  • @Deprecated:取消过期提示,方法过期了,但是还是需要用,禁止过期提示
  • @SuppressWarnings(”deprecation“忽略警告)
  • @SafeVarargs jdk1.7出现,对污染,不常用
  • @Functionalllbterface jdk1.8出现 配合函数式编程拉姆表达式,不常用

元注解

@Target 标名注解所在的位置,可以用在哪?

@Retention 标注注解的生命周期:在源文件里,字节码里,运行中

@Ingerited允许子注解继承

@Documented 生成javadoc会包含注解,不常用

@Repeatable注解为可重复注解,可以在一个地方使用多次,不常用

@Target (ElementType.)

ElementType.TYPE 应用于类的元素
ElementType.METHOD 应用于方法级
ElementType.FIELD 应用于字段或属性(成员变量)
ElementType.ANNOTATION_TYPE 应用于注解类型
ElementType.CONSTRUCTOR 应用于构造函数
ElementType.LOCAL_VARIABLE 应用于局部变量
ElementType.PACKAGE 应用于包声明
ElementType.PARAMETER 应用于方法的参数

@Retention (RetentionPolic.)

SOURCE 在源文件中有效(即源文件保留)

CLASS 在class文件中有效(即class保留)

RUNTIME 在运行时有效(即运行时保留)

自定义注解

public class TestAnnotation {  

    //2.标名注解使用的位置(可以加在类上 方法上 字段上)
    @Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})

    
    //3.本行代码表示自定义注解的生命周期(在于源文件中)
    @Retention(RetentionPolicy.SOURCE)

    //1.为注解添加功能,定义自定注解名Rice
    @interface Rice{
         //添加的功能    

         //自定义注解也可以赋予默认值,但是默认值的格式不能简写,而且变量类型后要加()
         int str() default "我是一个注解"
    }
}

//4.创建一个类用来测试自定义注解
@Rice
class TestAnno{
     @Rice
    public void eat(){
        System.out.println("我是一个注解");
    }
}

反射

它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

主要用于程序本身自己的调用,扩展延申功能,将目标交由程序管理(更改值/类,创建值/类)

反射的常用方法

获取反射的对象:

  1. Class.forName(类的全路径)
  2. 类名.class
  3. 对象.getClass

获取包名类名      

反射的对象.getPackage().getName()//包名

对象.getSimpleName()//类名

对象.getName()//完整类名

获取成员变量定义信息

getFields()//获取所有公开的成员变量,包括继承变量

getDeclareFields()//获取全部的成员变量,包括私有,但不包括继承

getField(变量名)//获取公开成员变量的内容信息

getDeclaredField(变量名)//全部的成员变量信息

获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)

获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)

反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法

反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。暴力反射API

 内部类

如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。
就是把类定义在类的内部的情况就可以形成内部类的形式。
A类中又定义了B类,B类就是内部类,B类可以当做A类的一个对象成员看待:只为A类服务,可以看做是外部类的一个特殊成员

2 特点

1) 内部类可以直接访问外部类中的成员,包括私有成员

2) 外部类要访问内部类的成员,必须要建立内部类的对象

3) 在成员位置的内部类是成员内部类

4) 在局部位置的内部类是局部内部类

成员内部类被private修饰

成员内部类被Private修饰以后,无法被外界直接创建创建对象使用

所以可以创建外部类对象,通过外部类对象间接访问内部类的资源

package cn.tedu.innerclass;
/**本类用来测试成员内部类被private修饰*/
public class TestInner2 {
	public static void main(String[] args) {
		/**怎么使用内部类Inner2的资源?*/
		//4.创建内部类Inner2对象进行访问
		//Outer2.Inner2 oi = new Outer2().new Inner2();
		//oi.eat();
		
		/**如果Inner2被private修饰,无法直接创建对象该怎么办?*/
		//7.创建外部类对象,间接访问私有内部类资源
		new Outer2().getInner2Eat();
	}
}
//1.创建外部类Outer2
class Outer2{
	//6.提供外部类公共的方法,在方法内部创建Inner2内部类对象,调用内部类方法
	public void getInner2Eat() {
		Inner2 in = new Inner2();//外部类可以访问内部类的私有成员
		in.eat();
	}
	//2.1创建成员内部类Inner2
	/**成员内部类的位置:类里方法外*/
	//5.成员内部类,被private修饰私有化,无法被外界访问
	private class Inner2{
		//3.创建内部类的普通成员方法
		public void eat() {
			System.out.println("我是Inner2的eat()");
		}
	}
}

静态资源访问时不需要创建对象,可以通过类名直接访问

访问静态类中的静态资源可以通过”. . . ”链式加载的方式访问

package cn.tedu.innerclass;
/**本类用来测试成员内部类被static修饰*/
public class TestInner3 {
	public static void main(String[] args) {
		/**如何访问内部类的show()?*/
		//4.创建内部类对象访问show()
		//方式一:按照之前的方式,创建内部类对象调用show()
		//Outer3.Inner3 oi = new Outer3().new Inner3();
		//oi.show();
		//方式二:创建匿名内部类对象访问show()
		//new Outer3().new Inner3().show();
		
		/**现象:当内部类被static修饰以后,new Outer3()报错*/
		//6.用static修饰内部类以后,上面的创建语句报错,注释掉
		//通过外部类的类名创建内部类对象
		Outer3.Inner3 oi = new Outer3.Inner3();
		oi.show();
		
		//7.匿名的内部类对象调用show()
		new Outer3.Inner3().show();
		
		//9.访问静态内部类中的静态资源--链式加载
		Outer3.Inner3.show2();
	}
}

//1.创建外部类Outer3
class Outer3{
	//2.创建成员内部类Inner3
	//5.内部类被static修饰—并不常用!浪费内存!
	static class Inner3{
		//3.定义成员内部类中普通的成员方法
		public void show() {
			System.out.println("我是Inner3类的show()");
		}
		//8.定义成员内部类的静态成员方法
		static public void show2() {
			System.out.println("我是Inner3的show2()");
		}
	}
}

匿名内部类属于局部内部类,而且是没有名字的局部内部类,通常和匿名对象一起使用

package cn.tedu.innerclass;
/**本类用来测试局部内部类*/
public class TestInner4 {
	public static void main(String[] args) {
		/**如何使用内部类的资源呢?
		 * 注意:直接调用外部类的show()是无法触发内部类功能的
		 * 需要再外部类中创建内部类对象并且进行调用,才能触发内部类的功能
		 * */
		//5.创建外部类对象调用show()
		//7.当在外部类show()中创建局部内部类对象并且进行功能调用后,内部类的功能才能被调用
		new Outer4().show();
	}
}
//1.创建外部类Outer4
class Outer4{
	//2.创建外部类的成员方法
	public void show() {
		//3.创建局部内部类Inner4—不太常用!!!
		/**位置:局部内部类的位置在方法里*/
		class Inner4{
			//4.创建局部内部类的普通属性与方法
			String name;
			int age;
			public void eat() {
				System.out.println("我是Inner4的eat()");
			}
		}
		/**如何使用局部内部类的资源?*/
		//6.在show()里创建内部类对象
		Inner4 in = new Inner4();
		in.eat();
		System.out.println(in.name);
		System.out.println(in.age);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sayhitoloverOvO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值