Java常用工具类之异常、包装类、字符串处理类、集合框架实现类、输入输出流、多线程

集合、多线程和I/O流等
介绍6种常用工具类:
1.如何应用异常处理程序中的问题?2.如何通过包装器类实现基本数据类型的对象化处理?3.字符串处理类String、StringBuilder是如何进行字符串信息操作的?4.常用集合框架及实现类使用?5.如何使用Java输入输出流进行文件读写?6.如何使用多线程实现数据并发通信?

1.3.1 Java异常

什么是异常处理机制以及如何实现Java中的异常处理。
Java异常:1.什么是异常?2.如何处理异常:1.try-catch-finally.2.throw.3.throws.4.自定义异常.5.异常链.
异常本质上是程序上的错误,包括编译期间和运行期间的错误.
在这里插入图片描述
Throwable类是Java当中异常的根类,它有两个重要的子类:Error(Error是程序无法处理的错误,表示运行应用程序中较严重的问题.代码运行时,Java虚拟机出现的一系列问题:Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关,是错误,如虚拟机错误VirtualMachineError、内存溢出OutOfMemoryError、线程死锁ThreadDeath等,且不可查,他们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况.对于设计合理的应用程序来说,即使确定发生了错误,本质上也不应该试图去处理它所引起的异常状况.因此不需关心Error类及其子类异常,但不希望出现这些异常.)、Exception(Exception是程序本身可处理的异常.异常处理通常只针对这种类型异常的处理.)
Exception异常分为两类;非检查异常Unchecked Exception(编译器不要求强制处理的异常RuntimeException及其相关子类,如空指针异常NullPointerException,数组下标越界异常ArrayIndexOutOfBoundsException,算数异常ArithmeticException,类型转换异常ClassCastException等),检查异常Checked Exception(编译器要求必须处置的异常.不处理编译无法通过,在Exception异常下的非检查异常之外的异常都称检查异常,如IO异常IOException,SQL异常SQLException等).
异常处理:在Java应用程序中,异常处理机制为:抛出异常,捕捉异常.可提升程序的健壮性。通过5个关键字来实现:try、catch、finally、throw、throws.捕获异常:try(执行可能产生异常的代码)、catch(捕获异常)、finally(无论是否发生异常代码总能执行).声明异常:throws(声明可能要抛出的异常).抛出异常:throw(手动抛出异常).
抛出异常:方法中出现错误引发异常时,方法会创建异常对象,并交付给运行时系统进行处理,此异常对象包含异常类型,异常出现时的程序状态等信息.
捕捉异常:当运行时系统捕获到这个异常后,运行时系统会逐步寻找合适的处理器,若找到与异常匹配的处理器会执行相关的处理逻辑,若没有找到程序会运行停止.
对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同.Java规定:对于可查异常必须捕捉、或声明抛出.允许忽略不可查的RuntimeException(含子类)和Error(含子类).
捕获异常try-catch-finally:
在这里插入图片描述
发生异常后try-catch块中try后面的语句不再执行。
。在这里插入图片描述
一般程序中非检查异常/运行时异常未被异常处理,导致程序中断运行。检查异常/编译时异常编译时已经捕捉或声明抛出了。
在这里插入图片描述
在这里插入图片描述
try块:用于捕获异常.catch块:用于处理try捕获到的异常.finally块:无论是否发生异常代码总能执行.try块后可接零个或多个catch块,若无catch块,则必须跟一个finally块.catch块和fianlly块不能单独使用.
控制台异常的查看:一般从最后一行看,最后一行是异常的最外层异常展示,向上深入.Exception类对象的printStackTrace()方法可打印错误的堆栈信息.
多重catch结构:多个catch块用于精准处理不同类型的异常,因为可能没有覆盖全部出现的异常,建议最后一个catch块写异常的父类Exception异常块收尾(此异常块放第一编译报错)处理各种类型异常.
System.exit(1);System类中的exit方法,当方法参数为非零数字时终止程序运行
后面的return会覆盖前面的返回值!程序遇见return;表示当前方法执行完成,就会退出方法!但是在try-catch-finally中比较特殊,退出方法之前要先执行finally块中的语句!
throws关键字:可通过throws关键字声明要抛出何种类型的异常,通过throw将产生的异常抛出。若一个方法可能出现异常,但没有能力处理这种异常,可在方法声明处用throws语句来声明抛出异常。throws语句用在方法定义时声明该方法要抛出的异常类型。
在这里插入图片描述
当方法抛出异常列表中的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。
通过throws抛出异常时,针对可能出现的多种异常情况,解决方案:1.throws后面接多个异常类型,中间用逗号分隔。2.throws后面接Exception,调用该方法的方法必须有捕捉Exception异常的处理。
可通过添加文档注释提示你调用该方法时该方法可能会出现它声明的异常。
当throws非检查型异常时,在调用方法处,编译器不会要求对异常进行处理
throw关键字:throw用来抛出一个异常(抛出一个异常类的实例化对象)。eg:throw new IOException();throw抛出的只能够是可抛出类Throwable或其子类的实例对象
使用throw关键字的优点:1.规避可能出现的风险。2.完成一些程序的逻辑。
throw抛出异常对象的处理方案:1.通过try…catch包含throw语句–自己抛自己处理。2.通过throws在方法声明出抛出异常类型(此时只能抛出与throw对象相同的类型或其父类)–谁调用谁处理–调用者可自己处理,也可继续上抛。
throw手动抛出的异常不建议是非检查类型,因为编译器不报错。
如果抛出的异常对象是Checked异常(除了RuntimeException及其子类的其他Exception),则要 (1)处于try块里被catch捕获或者(2)放在一个带throws声明的方法里;思考:(1)(2)是否多次一举。
如果抛出的是RuntimeException则既可以显示使用try…catch捕获也可以不理会它 。
使用Java内置的异常类可描述在编程时出现的大部分异常情况。也可通过自定义异常描述特定业务产生的异常类型。所谓自定义异常,就是定义一个类,去继承Throwable类或其子类
快捷键Ctrl+t或Ctrl鼠标左击进入jar包看源码或直接在工程-JRE System Library-rt.jar包下查找。看源码主要看:1.需了解Java的23种基本设计模型。2.需了解框架的真正实现类(配置与运行结合看)。3.多找一些源码包进行解析。rt.jar包是Java基础类库。
异常链:有时会捕获一个异常后再抛出另一个异常。顾名思义就是:将异常发生的原因一个传一个串起来,即把底层的异常信息传给上层,这样逐层抛出.
最后输出的异常信息就只有最后一个。若想把前面所得异常信息都输出,需保留异常信息,有两种方式:1.通过构造方法对旧异常对象的获取。Throwable(String message,Throwable cause)–保留底层异常的异常信息。2.通过initCause(Throwable cause)方法(一个旧异常的信息来初始化一个新的异常)用来获取原异常的信息,其中cause是原异常的对象。
总结:
实际应用中的经验与总结:1.处理运行时异常时(非检查异常),采用逻辑去合理规避同时辅助try-catch处理。2.在多重catch块后面,可加一个catch(Exception)来处理可能会被遗漏的异常。3.对于不确定的代码,也可加上try-catch,处理潜在的异常。4.尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出异常信息。5.具体如何处理异常,要根据不同的业务需求和异常类型去决定。5.尽量添加finally语句块去释放占用的资源。如关闭文件、关闭数据库的链接等。

1.3.2 Java包装类

基础数据类型和包装类之间的转换。
什么是包装类?包装类与基本数据类型之间的对应关系?包装类的常用方法?
在这里插入图片描述
基本数据类型没有对象的特征,没有属性、方法,无法对象化交互。但通过包装类可使基本数据类型拥有属性、方法,可对象化交互。
在这里插入图片描述
基本数据类型的数值型的包装类都继承Number类,基本数据类型的字符型和布尔型继承Object类。包装类都是java.lang包下的类。
基本数据类型和包装类之间的转化:装箱:基本数据类型的值转换为包装类对象。拆箱:包装类对象转换为基本数据类型的值。
装箱和拆箱都有自动和手动两种方式。装箱:1.自动装箱。eg:int t1=2; Integer t2=t1;(JDK自动创建了对象默认调用valueOf()方法,在常量数组缓存区又称对象池范围内只会创建一次对象,不在范围会创建多次对象。当使用new时,不管在不在常量数组缓存区又称对象池范围内,new Integer(t1),new多少次就创建多少对象;便于代码书写)。2.手动装箱。eg:Integer t3=new Integer(t1);拆箱:1.自动拆箱。eg:int t4=t2;2.手动拆箱。Int t5=t2.intValue();(默认调用包装类对象名.intValue()方法)实际上自动装/拆箱是自动完成了手动装/拆箱的过程
Shift+向上的箭头快捷键选中上一行,后可复制粘贴。
System.out.println();打印对象时默认调用toString()会打印类名加上@加上对象的哈希值,但打印包装类的对象时,因为包装类重写了toString()方法会直接打印对象值的字符串。
基本数据类型和字符串之间的转换:
在这里插入图片描述
在这里插入图片描述
parseInt(t2)方法返回值是int型,valueOf(t2)方法返回值是Integer对象
int t4=Integer.valueOf(t2)隐含调用t2.intValue()方法,将Integer对象转换成int型值。
在java中,两个类并不能直接转换,这是语法规则。实际通过该类实例一个新对象保存另一个类的值
在这里插入图片描述
包装类对象的默认值都是null
包装类对象间的比较:对象和对象做比较(用==)是比较两者之间地址是否相等。Java提供了一个缓存区机制,当数值在**[-128,127]时,将数据放入对象池(缓存区,也有称常量池**!=JVM常量池)中。每次赋值,去取值时,从常量数组缓存区又称对象池中获取,所以,两次地址是相等的(eg:Integer num=100;相当于Integer num=Integer.valueOf(100)😉。数值不在[-128,127]之间,每次赋值时会通过new关键字创建新的对象,两次地址不等。当包装类对象的值与值类型的值相等,两者作比较时包装类对象会自动拆箱,结果为true。
包装类的常量数组缓存区/对象池是对包装类而言的,专门是指包装类自带的缓冲区,是一个数组(在Integer源码中就有一个内部类IntegerCache,这个内部类里面定义了一个数组,这个数组就是来放我们的缓存数据的。)。和JVM常量池不一样。
常量池会存放Long,Integer,Byte,Short类型在[-128,127]范围中的数值。Character类型的数值存储在常量池的范围是[0,127]。Boolean类型在常量池中的范围为true,false。而Float,Double类型没有常量池的概念。所以,常量池中不会存放这两种类型的数值。
缓存区:专门是指包装类自带的缓冲区,可以存放包装类中的内容
包装类的对象池/缓存区(也有称常量池)和JVM的静态/运行时常量池没有任何关系
JVM中存在堆,栈,方法区,常量池的内容
在这里插入图片描述
堆中存的是对象,存放所有new出来的对象
栈中存的是基本数据类型和堆中对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中
方法区:方法区存放类的信息(包括类的字节码,类的结构)、常量、静态变量等。字符串常量池就是在方法区中
常量池(!=包装类常量池):常量池是Java中专门用来存储常量的区域,存放字符串常量和基本类型常量如100,12.5等基本数据类型的数据,还有String类型的值如"123"都是存储到常量池中的。
基本数据类型都是直接存储在内存中的内存栈上的,数据本身的值就是存储在栈空间里面
电脑内的存储器分为缓存(是CPU与内存之间的临时存储区,即缓存区不属于内存)+内存(包括堆、栈、方法区、常量池).
自动装箱操作时,当参数在[-128,127]时,自动装箱使用的是Integer.valueOf(),当参数不在这个范围内,Integer.valueOf()等同于使用new Integer()的方式创建新对象完成装箱操作。自动拆箱使用的是intValue()方法。
基本数据类型使用‘’进行比较,则比较的是他们的值是否相等。引用类型使用‘’进行比较,则是比较他们在内存中的存放地址是否相等。
对象池是内存中的一块区域,使用的基本思路是:将用过的对象保存起来,等下一次需要这个对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。
可以对包装类对象进行运算,相当于其对应的基本数据类型进行运算。Eg:但当Integer对象只有默认值时和int型值可进行算数运算,编译不报错,运行报空指针异常。当Integer对象被赋值后可和int型值可正常进行算数运算。

1.3.3 Java字符串处理类

字符串的应用,String类和StringBuilder类的常用方法。
学习字符串常用处理类String、StringBuilder。1.如何创建String对象?2.String对象的常用方法?3.和equals方法的区别?4.String的不可变性?
创建String对象的3种常见方式(不止3种):
1.在这里插入图片描述
会产生1个或0个对象,编译时会在内存的字符串常量池中先去找有没有一个 “imocc” 的字符串常量,因为常量池中相同的字符串只会有一个,所以如果已经有 “imocc” 这个字符串常量就让s1指向这个常量,如果没有就在字符串常量池中创建一个 “imooc” 对象且让s1指向这个常量。
2.在这里插入图片描述
3.在这里插入图片描述
方式2和3使用new关键字都会产生一个或两个对象,一定会在堆中创建一个对象,它的值为””或”imooc”。并且将堆中这个对象的引用(而不是常量池中对象的引用,所以s3
s1输出false,因为一个是堆中对象地址一个是常量池中对象地址。)交给s2/s3持有。然后若””或”imooc”这个字符串在常量池中不存在,会在常量池创建一个对象,它的值是””或”imooc”。若存在这个串则不需要创建常量池中的对象
String的常用方法:
在这里插入图片描述
字符串和byte数组间的相互转换与GBK编码和UTF-8编码的编码问题以及UnsupprotedEncodingException不支持编码异常问题:
字符串–>byte[]数组:String s1=”Java 编程 学习”; byte[] b=s1.getBytes();转换的编码格式由开发软件设置决定。getBytes()方法括号里字符集(charset)参数为”GBK”或”UTF-8”转换为对应的编码格式
byte[]数组–>字符串:String s2=new String(b);转换的编码格式由开发软件设置决定。String()构造方法的第二个字符集(charset)参数为”GBK”或”UTF-8”转换为对应的编码格式
将编码方式设置为UTF-8能最大程度匹配所有文字,减少解析文件时产生的乱码,是开发中常用的编码形式。UTF-8(是在互联网上使用最广的一种 Unicode 的实现方式)不仅能表示中文的字符,也能表示外国的许多文字,会在网页等内容解析时,最大程度的匹配到所有文字。而GBK(国人扩展ASCII支持中文编码)则是最大程度表示中文字符,对外国的多文字是不支持的
等于运算符与equals方法的区别:
基本数据类型使用‘’等于运算符进行比较,则比较的是他们的值是否相等。引用类型使用‘’进行比较,则是比较他们在内存中的存放地址是否相等。在Object中,equals方法和等于运算符都是比较地址。在String类中对equals方法进行了重写,用equals方法比较的是两个对象的内容是否相等
在这里插入图片描述
String的不可变性:String对象一旦被创建,则不能修改(字符串常量池原String对象不变),是不可变的(字符串常量池原String对象在堆中地址不变)。所谓的修改其实是创建了新的对象(常量池重新分配String新对象内存,对象引用指向字符串常量池中新的对象,两对象堆中地址不同),所指向的内存空间不变(字符串常量池中原String对象地址不变)。
+与concat()方法的区别如下(常量池中只有常量,没有中间变量):1.concat 只将指定字符串连接到此字符串的结尾。2.+是可以把任何类型的数据连接起来!+默认是java的String类的一种重载,将+后面的对象,转换为String类型,然后再进行字符串拼接。3.这两者其实都产生一个新的对象(储存在堆中) 。
String和StringBuilder的区别:String具有不可变性,而StringBuilder不具备。
建议:当频繁操作字符串时,使用StringBuilder。可减少产生多余变量和废弃常量(对象),节省内存空间。
StringBuilder和StringBuffer两者基本相似。StringBuffer是线程安全的,StringBuilder是没有,所以StringBuilder性能略高。因为线程安全时相对速度慢一些大部分情况下,进行字符串处理时,都是单线程,而线程安全主要针对多线程
StringBuilder中常用方法:
在这里插入图片描述
在这里插入图片描述
String对象能使用String a=“你好”;而Stringbuilder不能用"="来赋值。String可类似基本类型的赋值方法来创建对象,是由于String比较常用,所以底层给它做了优化处理并有字符串常量池,并不是所有的类都有常量池。一般引用类型构造一个新的对象都需要使用构造方法来构造。
反编译:将.class文件反编译成.java文件。
总结:进一步掌握javaApi文档的使用。

1.3.4 Java集合框架及实现类使用

List、Set和Map三种集合的特点、存储方式和主要实现类的使用。

集合:概念?体系结构?实际应用?
Java中的集合是工具类,可存储任意数量的具有共同属性的对象
集合是一堆接口和类的总称
为什么使用集合,而不用数组?数组长度固定,集合长度可动态改变
应用场景:1.无法预测存储数据的数量。2.同时存储具有一对一关系的数据。3.需要进行数据的增删(数组进行此类操作繁琐)。4.解决数据重复问题。
集合框架的体系结构:分为两种,Collection接口(存储类的对象):有三个子接口List序列、Queue队列、Set集。其中List、Queue是有序允许重复,Set无序不允许重复,每个接口都有各自的实现类,eg:ArrayList是List的主要实现类,可看作长度可动态增长的数组.LingkedList是List和Queue的主要实现类,表示链表的内容.HashSet是Set的主要实现类.即哈希集Map(存储键值对):HashMap是Map的主要实现类,即哈希表(存储以键值对表现形式的数据)。
在这里插入图片描述
主要介绍实现ArrayList,HashSet,HashMap三个类的内容。
集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算,如果直接使用类的方式,整个集合将没有扩展性,代码没有复用性,而且为以后集合的后续开发也产生很多困难。
List(列表),List是元素有序并且可重复的集合,称为序列。List可精确的控制每个元素的插入位置,或删除某个位置的元素。List的两个主要实现类是ArrayList和LinkedList.
ArrayList:1.底层是由数组实现的。2.动态增长,以满足应用程序的需求。3.在列表尾部插入或删除数据非常有效(其他位置需要更多操作消耗内存资源较大)。4.更适合查找和更新元素。5.其中的元素可以为null
在List中存储并操作字符串信息比较方便,因为字符串类是JDK系统自带的类并且可以直接使用(像基本数据类型而不像其他引用类型需要new关键字创建对象)。
自定义的类名不要与系统的类名相同,否则Java会先调用本类的同名构造方法,编译报错。
集合Collection中存放的是对象的内存地址。数组存储对象时,存储的对象的内存地址。都不是存储对象的本身。
案例:公告管理(一般电商网站和企业门户网站在首页中的一个指定区域会用滚动形式显示信息)
在集合ArrayList中添加自定义的类的对象。使用集合ArrayList管理和操作这些数据。后面学习数据库,会在数据库中管理和操作这些数据。
需求:1.公告的添加和显示2.在指定位置处插入公告3.删除公告4.修改公告。
在这里插入图片描述
Set:Set是元素无序并且不可以重复的集合,被称为集。其主要实现类是HashSet,哈希集.
HashSet:1.中的元素无需并且不可重复(插入重复元素,会插入失败,但不会报错)。2.只允许一个null元素。3.具有良好的存取和查找性能。HashSet的底层是HashMap
插入重复元素,会插入失败,但不会报错的解释:当调用add(Object)方法时候,首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;如果已存在则调用Object对象的equals()方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素
HashSet的底层是HashMap的解释:HashSet是借助HashMap来实现的,利用HashMap中Key的唯一性,来保证HashSet中不出现重复值。
Set集合无序是指不会按照其添加的顺序来存储对象。set集合里对象在集合中是通过对象的hashcode值进行排序的
Set中的equals(Object o)和hashCode()方法相当重要
在这里插入图片描述
在这里插入图片描述
Iterator迭代器:Iterator接口可以以统一的方式对各种集合元素进行遍历。迭代器有两个重要的方法:hasNext():检测集合中是否还有下一个元素。next():返回集合中的下个元素。迭代器的遍历流程:
在这里插入图片描述
在这里插入图片描述
set.iterator();是将set集合放入Iterator迭代器中,输出时,对迭代器中的元素进行遍历。
迭代器遍历一次后需要重新赋值(eg:it=set.iterator();若不重新赋值则下次遍历无数据输出.),不需要重新声明。
在集合set的元素添加中,对基本数据类型是不能插入重复的值,而对象类型的插入不能插入重复的对象.(不能插入同一对象,同类型的内容一样的两个对象可插入)。若想同类型的内容一样的两个对象不可插入,则需重写这个类型的equals方法(判断两个对象的内容是否一致,一致返回false),hashCode()方法重写后不需要改变,系统会自动将对应的属性等信息更改,所以直接使用系统提供的即可:
add方法添加的时候会进行的底层的调用,先调用hashCode方法比较,如果hashCode相同(同类型的对象间的hashCode是相同的(重写hashcode方法)) ,再调用equals方法比较 ,如果hashCode不同,说明不是一个对象,可以直接添加 。
在Java中,哈希码代表对象的特征。哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。几个常用的哈希码的算法:
1、Object类的hashCode.返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样
2、String类的hashCode.根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串所在的堆空间相同,返回的哈希码也相同。
3、Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100),i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。
自定义类重写hashCode方法,new对象不初始化的同类型的对象间的哈希码相同,对象间属性赋相同值后的哈希码一样,对象间属性赋不同值后的哈希码不一样;重写hashCode方法后,不同类型的对象对应的属性类型和属性值相同时的hashCode方法得到的哈希码一样,equals方法返回false也会插入成功,通过定义集合的泛型约束集合只添加一种类型的对象
关于HashSet的add()方法,底层是使用HashMap的put()方法来判断插入的数据和set集合中已有的值是否一致的。
在这里插入图片描述
这里this是马上加入的集合的元素,obj是集合中的hashCode已划分的3分之一中的元素
这里的obj.getclass()表示通过java反射的方式,根据对象来获取类的对象的信息,obj.getClass()就是获取传入参数obj对象的类型信息。但obj还是Object类型,所以需要强转一下。
在这里插入图片描述
上述hashCode方法是将所有属性通过一系列算法,将所有属性通过计算哈希码来减少属性的重复性,同类型的对象间的hashCode是相同的(重写hashcode方法)
在HashSet集合中添加自定义的类的对象。
案例:宠物猫信息管理
在这里插入图片描述还有若干属性。
查找元素:1.直接通过对象查找,使用contains方法2.通过属性查找,遍历集合中所有对象,并使用get方法结合equals方法来寻找符合条件的对象.
引入泛型(Generic):在集合类型后面<指明集合存储的对象的类型>和在实现类构造方法名与()之间添加<指明集合存储的对象的类型>。迭代器也需要这样。Eg:Iterator<指明迭代器存储的集合中存储的元素类型/元素的类型> 迭代器变量名;
Set集合遍历时底层会比较集合中的每一个元素,所以使用remove()方法删除集合中的元素时需注意:1.当此元素不是集合的最后一个元素(若是最后一个元素则不需添加break语句),运行时会报错,因为删除这个元素后Set集合的存储结构会变化从而影响后面循环的继续,解决方案:可在删除此元素后添加break;跳出循环。2.若需删除集合中多个元素,在remove()语句后添加break语句只会删除一个满足条件的元素与需求不同,解决方案:可添加这些元素到新集合,然后用removeAll(新集合);方法删除原集合中的所有新集合中的元素。
Map集合:Map中的数据是以
键值对(key-value)的形式存储
的。(key-value)以Entry(Entry是接口)类型的对象实例存在。可通过key值快速地查找value。一个映射(key-value的映射)不能包含重复的键(key值唯一,value值可重复)。每个键只能映射一个值。Map集合的主要实现类是HashMap.
HashMap:1.基于哈希表的Map接口的实现。2.允许使用null值和null键(键值唯一,所以只能有一个null键)。3.key值不允许重复。4.HashMap中的Entry对象是无序排列的。
静态接口:使用static关键字修饰的接口. 静态接口不可直接定义,必须在类的内部定义,类似于静态内部类
Map中采用Entry内部类来表示一个映射项,映射项包含Key和Value
将接口的实现类实例的对象赋值给该接口类型的引用变量,提高程序的扩展性,便于程序代码的重构,体现了面向接口编程的好处。将子类实例的对象赋值给父类的引用变量,提高程序的扩展性,便于程序代码的重构,体现了面向对象编程的好处。
HashMap集合中添加键值对形式的字符串:
案例1:在这里插入图片描述
案例2:商品信息管理
在这里插入图片描述
程序在处理重复key值的时候,自动保留了后面一个value.判断商品编号是否存在,对象.containsKey(Key值),可以测试Key值是否重复.
在这里插入图片描述

调用Scanner类从键盘接收数据的方法赋值给变量时,如果接收数据不匹配运行时会报错,若不对输入的值进行存放处理,下次循环调用时会将此数据赋值给下一个接收数据的方法中,所以需要在异常捕捉cath块中增一条next()方法对之前接收的数据进行存储处理,
在这里插入图片描述

1.3.5 Java集合排序

对基本数据类型以及自定义类的数据进行排序。
集合排序主要内容:1.集合中的基本数据类型排序。2.集合中的字符串排序。3.Comparator接口。4.Comparable接口
数组的排序是调用Arrays类中的sort()方法。集合排序使用Collections类的sort()方法。sort(List list)–根据元素的自然顺序对指定列表按升序进行排序。
泛型中的数据类型不能使用基本数据类型要使用包装类.
Set和Map无序,Set和Map其实要看他们的实现类,HashSet和HashMap是无序的,但是TreeSet和TreeMap是有序的,可以排序
实现Comparator或Comparable接口可对集合中的自定义类的对象排序
Comparator接口:1.强行对某个对象进行整体排序的比较函数。2.可将Comparator传递给sort方法(如Collections.sort()或Arrays.sort()).方法:
在这里插入图片描述
升降序是根据底层实现完成的。在进行排序时,如果两个数比较结果大于0,就把前一个数和后一个数交换,也就是把大的数放后面了,即所谓的升序了。如果第二个参数与第一个参数调换顺序,也就是降序了。
在这里插入图片描述
Comparable接口(java.lang包下的一个接口):1.此接口强行对实现它的每个类的对象进行整体排序。2.这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。3.对于集合,通过调用Collections.sort()方法进行排序。4.对于数组,通过调用Arrays.sort()方法进行排序。
在这里插入图片描述
Comparable与Comparator:
区别:实现了comparable的对象直接就可以成为一个可以比较的对象,不过得在类中进行方法定义;comparator在对象外比较,不修改实体类。
场景:一般情况下如果对某个类进行排序,比如Cat类,如果使用Comparable接口的方式,那么Cat类需要实现Comparable接口。如果Cat类通过Comparable接口的方式实现排序,比如通过name排序了。那么我们还希望通过age进行排序,这时不希望修改Cat类,那此时就需要使用Comparator接口了。Comparable也仅有一种比较规则.Comparator可以实现多个,来提供多个比较规则。
使用场景的考虑时,一般Comparable接口可以作为实现类的默认排序算法,Comparator接口则用于一个类的扩展排序
在这里插入图片描述

1.3.6 Java泛型

介绍泛型作为方法参数、自定义泛型和自定义泛型方法等内容。
为什么使用泛型?在Java中添加泛型之前,泛型程序设计使用继承来实现的。坏处:1.需要强制转换。2.可向集合中添加任意类型的对象,存在风险。好处:不用进行强制类型转换,避免运行时异常的安全隐患,提高java程序的类型安全,用泛型可使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设
泛型不能是基本数据类型。泛型主要用在集合List和集合Map中
泛型的使用:
在这里插入图片描述
在这里插入图片描述
主要内容:1.泛型作为方法参数。2.自定义泛型类。3.自定义泛型方法
1.泛型作为方法参数:
1.变量声明的类型必须与传递给实际对象的类型一致。2.若想将实际对象的类型为子类传递给声明变量类型为父类时,需在声明变量类型为父类的泛型类型设置为<? extends 父类名> 父类引用变量名
在这里插入图片描述
extends后可跟引用类型也可跟接口。
在这里插入图片描述
extends后可跟引用类型也可跟此引用类型的超类/父类。
2.自定义泛型类:在定义的类名后加<T、X或E,可定义多个泛型类型>,定义部分属性类型为T或E,实例化此类时:在声明类型引用变量中的类型名后边添加泛型类型(即<引用类型>),JDK7以后构造方法可省略泛型类型。自定义泛型类实际是将类型参数化了。泛型类型参数化,由参数决定.泛型类型符号任意,一般大写,常用T、X或E。尖括号里面的内容,表示可以传入任何的类型。
应用场景:1、若项目中,需针对多个泛型方法中传入的参数类型,做统一的类型限制时,就需用到自定义泛型类了。2、自定义泛型类,可在使用时传入限定类型,对应实例化时,根据传入的类型来赋值和取值,使这个类更加灵活。
3.泛型方法(泛型方法不一定写在泛型类里):<>只能在访问修饰符和返回值类型之间,尖括号里的内容可以是常用的T、X、E也可?/T/X/E extends 引用类型表示T、X、E或引用类型及其子类。方法的参数列表可包含若干泛型类型。
声明泛型方法修饰符后边<>这里是写具体的泛型的而不是用<? extends Animal>,这样会报错,因为这样只有null可以添加到这个list中去.
区别T和?:
1."和"<?>",首先要区分开两种不同的场景:主要用于“声明一个泛型类或泛型方法”。<?>主要用于“使用泛型类或泛型方法”。例如,定义一个泛型类,class School是正确的,但class School<?>就是错误的。
2、T限制了类型,?只是通配符。 表示后续都只能使用T进行某些判断或操作,而<? extends ClassA>表示后续使用时只要是ClassA的子类即可。
<? extends Animal> 这里?代表一个未知的类型,但是这个未知的类型实际上是Animal的一个子类,Animal是这个通配符的上限。而中的T是Number子类一个确定类型。

1.3.7 Java多线程

能进行多线程程序的编写,并掌握生命周期、同步和线程间通信等内容。
线程的概念和生命周期,如何在Java中创建和启动线程,线程的优先级,以及线程的同步和死锁问题。
什么是线程?
进程:进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程。
一个进程中可以有一个线程执行,称为单线程,一个进程也可以有多个线程执行,称为多线程.
线程:线程是比进程还要小的运行单位,一个进程包含多个线程。可看成一个子程序。
CPU使用时间片轮转的工作方式,可以让多个程序轮流占用CPU,达到同时运行的效果.
对于CPU来说是轮流运行很多程序(每个程序运行的时间很短),但对用户来说感觉不到这个时间间隔,会等同认为这些程序是同时运行。
主要内容:1.什么是多线程?2.线程的创建3.线程的状态和生命周期。4.线程调度。5.同步与死锁。
线程的创建:1.创建一个Thread类,或一个Thread子类的对象。2.创建一个实现Runnable接口的类的对象。多线程创建的第三种方式参见教辅:创建多线程的第三种方式.pdf。
Thread类是一个线程类,位于java.lang包下,实现了Runnable接口。
Thread类的常用构造方法:
在这里插入图片描述
Thread类的常用方法:
在这里插入图片描述
不同功能的线程之间的区别就是run()方法内容不同.run()方法又称为线程体方法,是线程中最重要的方法
Runnable接口:1.只有一个方法run();2.Runnable是Java中用以实现线程的接口。3.任何实现线程功能的类都必须实现该接口
使用start()方法启动线程一个线程只能启动一次,多次启动编译不会报错,运行时出现异常
线程获取Cpu的使用权是随机的
创建一个实现Runnable接口的类的对象:
为什么要实现Runnable接口?1.Java不支持多继承.不打算重写Thread类的其他方法.
在一个java文件中,可以有多个Class,但只能有一个是用public 修饰的Class,且和文件名一致,其他的class一般不加访问权限修饰,这是一种常见的Class的定义方式。
多个线程可共享一个资源
线程的生命周期:
在这里插入图片描述
线程的状态:新建(New,即创建Thread类或其子类的对象后)、可运行/就绪(Runnable,即线程对象调用start()方法后,此线程并不是立即运行而是为可运行状态,等待cpu的使用权,只有拥有cpu使用权时此线程才运行.)、正在运行(Running,拥有cpu使用权时此线程才运行)、阻塞(Blocked,当线程遇到干扰不再执行)、终止(Dead).
sleep方法的使用:是Thread类的方法:public static void sleep(long milis);作用:在指定的毫秒数内让正在执行的线程休眠(暂停执行,该时间段其他线程无法拥有cpu使用权).参数为休眠的时间,单位是毫秒。在调用sleep( )方法时需捕获一个中断异常。应用场景:1.计时。2.控制刷新频率
join方法应用:是Thread类的方法:public final void join();作用:等待调用该方法的线程结束后才能执行.public final void join(long millis);作用:等待该线程终止的最长时间为millis毫秒。
调用join( )方法可以使其他线程由正在运行状态变成阻塞状态。
线程优先级:1.Java为线程类提供了10个优先级(数字越大优先级越高)。2.优先级可用整数1-10表示,超过范围会抛出异常。3.主线程默认优先级为5。4.还可用优先级常量表示优先级(-MAX_PRIORITY:线程的最高优先级10;-MIN_PRIORITY:线程的最低优先级1;-NORM_PRIORITY:线程的默认优先级5;)。
优先级相关的方法:
在这里插入图片描述
线程优先级的作用是通过设置优先级,虽然执行顺序可能是随机的(执行顺序不仅和优先级有关还和操作系统的环境、CPU的工作方式等相关),但是大大提高了优先级高的线程优先执行的概率。例如:如果有大量线程都被堵塞, 都在等候运行, 程序会尽可能地运行优先级高的那个线程,但优先级较低的线程也有可能会运行。
线程同步:
同步是在同一时刻只有一个线程可以执行这个方法或语句块
多线程运行问题:各个线程是通过竞争CPU时间而获得运行机会的.各线程什么时候得到CPU时间,占用多久,是不可预测的.一个正在运行着的线程在什么地方被暂停时不确定的.
案例:银行存取款问题:存取款两个线程可能交叉执行,导致运行结果出现问题。为了保证在存款或取款时,不允许其他线程对账户余额进行操作。需将Bank银行类对象进行锁定。使用关键字synchronized实现锁又称对象监视器
关键字synchronized确保共享对象在同一时刻只能被一个线程访问。这种机制称线程同步/线程互斥
关键字synchronized用在:成员方法(作用范围是整个方法,作用的对象是调用此方法的对象)、静态方法(作用范围是整个方法,作用的对象是这个类的所有对象)、语句块(将this加锁,作用范围是整个语句块,作用的对象是调用此语句块的对象),synchronized修饰后的线程同步是指不同线程访问同一个对象,每次只能有一个线程访问这个对象(即同一个时间只能有一个线程得到执行,另一个线程必须等待当前线程执行完这个方法或者代码块以后再执行另一个线程的方法或者代码块.),而不是具体访问这个对象中的某个方法。:
在这里插入图片描述
线程间的通信:
银行存取款案例中问题:账户余额不够了怎么办?–等待存入足够的钱后处理.这时需要线程间的通信了。案例:生产者消费者案例(生产1个消费1个)。
在这里插入图片描述
wait()方法,使当前线程进入阻塞状态,会释放锁资源,让出系统资源。
线程间通信调用wait()方法使线程等待,所有调用wait()方法的线程可能同时都处于等待的情况,也称死锁,此时可用notify()或nitifyAll()方法唤醒等待的某个/所有线程,建议使用nitifyAll()方法唤醒所有等待的线程。notify()和nitifyAll()方法是与wait()方法对应的。
使用wait()方法阻塞的线程,必须用notify()或notifyAll()方法进行唤醒
wait( ),notify( ),notifyAll( )这三个方法是继承于Object的,所以任何类都是可以直接调用的
在这里插入图片描述

1.3.8 Java输入输出流

使用字节流和字符流进行数据的读写,以及对象的序列化与反序列化(对象的序列化和反序列化就是使用字节流对java对象的读写)问题。
输出流:
在这里插入图片描述
就是指一连串流动的字符,以先进先出的方式发送信息的通道。
输入流;
在这里插入图片描述
文件输入–读,文件输出–写
主要内容:1.File类的使用(文件/目录的创建、获取文件/目录的属性)。2.字节流(传输二进制的字节型数据)。3.字符流(传输abc等char类型的字符数据)。4.对象的序列化与反序列化(对象的读写)
File类:
什么是文件?文件可认为是相关记录或放在一起的数据的集合。
在Java中,使用java.io.File类对文件进行操作,此节内容中所有的类/接口都是位于java.io包中.
Windows中的目录分隔符为“\”,Linux中的目录分隔符为”/”.
java.io.File类对文件进行操作;canRead() 文件是否可读;canWrite() 文件是否可写;exists() 文件或目录是否存在;getName() 获取文件名;getParent():返回文件父目录路径; getPath():返回文件的相对路径; getParentFile():返回文件所在文件夹的路径;getAbsolutePath()用来返回文件的绝对路径;isDirectory() 是否是目录;isFile() 是否是文件;isHidden() 是否是隐藏文件;mkdir() 用于创建单级目录;mkdirs() 用于创建多级目录;
第一个“\”作为转义字符.File类型的对象名.createNewFile();创建文件,但
没有文件扩展名
.
File 类是对文件系统的映射,并不是硬盘上真实的文件。所以new File(“xxx.xxx”) 只是在内存中创建File文件映射对象而并不会在硬盘中创建文件。若需要创建文件需要以下操作:先判断映射的文件是否真实存在 file.exists(),如果存在即可直接操作, 否则需要调用file.mkdir();创建真实目录,或file.createNewFile();创建真实文件。"xxx.xxx"路径若不完整则默认会创建在当前工程目录下。
只有文件/目录在外存存在的时候,使用 isDirectory()、isFile() 才能判断文件/目录为true,否则为false。
file1.createNewFile()方法创建文件,若调用的File路径中无文件的扩展名,就会创建无扩展名的文件.
绝对路径:是从盘符开始的路径。
相对路径:是从当前路径开始的路径。三种情况:1…java程序与已经创建的文件在同一目录:File f1=new File(“文件名 eg:xxx.txt”)。2.源程序在已经创建的文件上一级目录:File f1=new File(“文件父目录路径\文件名 eg:xxx\xxx.txt”)。3.源程序在已经创建的文件上一级的另一个文件夹中:File f1=new File(“…\文件父目录路径\文件名 eg: …\xxx\xxx.txt”)。其它情况延伸情况3. …\返回上一级目录。
字节流:字节输入流InputStream 字节输出流OutputStream
在这里插入图片描述
在这里插入图片描述
读写的都是二进制形式的数据
FileInputStream文件输入流:1.从文件系统中的某个文件中获得输入字节(复制粘贴即是文件的读写过程.)。2.用于读取诸如图像数据之类的原始字节流.
常用方法:
在这里插入图片描述
若read()方法的返回值为-1,则表示已经达到文件末尾
未读完文件无参/有参read()方法的返回值是读取的字节数据/读入到byte数组中的字节总数.
参数off表示从哪个位置处开始存放,又叫偏移量。
文件读取完后必须调close()函数关闭文件释放资源。
FileOutputStream文件输出流:写字节数据到某个文件中。
常用方法:
在这里插入图片描述
字节流适合图片等多媒体文件的复制粘贴等操作,但不适合处理字符相关的文件(文件里写入的跟显示的不一致).
调用write(byte[] b或byte[] b,int off,int len)方法把byte数组中的内容写出来(同时数组被清空了).
要实现文件的拷贝,应该先读取文件数据,再将读取到的数据写入到指定文件。要保证拷贝前后文件的大小不改变,在写入数据时应选用带有长度的方法write(byte[]b,int off,len),做到实际读取到多少数据就写多少数据,就避免了会改变文件大小的问题。Eg:文件输出流对象名.write(b,0,n) 这样子保证拷贝的文件和原来的文件一样大,从0开始 读取n个字符 ,n!=-1的时候表示读取多少个字符.

FileInputStream fis = new FileInputStream("D:\\UserFiles\\Photos\\horse By nateberrett.jpg");
FileOutputStream fos = new FileOutputStream("D:\\UserFiles\\Photos\\a.jpg");
int len;
byte[] b = new byte[1024];
while ((len=fis.read(b)) != -1) {
      System.out.println(len);
      fos.write(b,0,len);
}

缓冲流:分为缓冲输入流BufferedInputStream和缓冲输出流BufferedOutputStream.没运用缓冲流是直接在硬盘中读写,读写速度慢,运用缓冲流是经过缓冲区(内存)再在硬盘中读写可提高读写速度
在这里插入图片描述
BufferedInputStream和BufferedOutputStream都有一个字节数组来存储缓冲的数据,但是这个数组是不可见的;
缓冲区可看做一块特殊区域,可容纳一定大小的数据,在java虚拟机中,直接从内存中读取数据是最快的,而缓冲区是内存中的一块区域,所以使用缓冲区的输入输出流运行效率是比较高的。
缓冲输入流BufferedInputStream不能直接读取文件系统中的数据,缓冲区满了会自动执行写操作,缓冲区不满就不会进行写操作,所以,当缓冲区未被填满但要执行写操作时就要强制清空缓冲区调用flush方法/close方法,建议都写flush方法,并写在close关闭流方法之前.).
流关闭的顺序:建议在关闭流时,先打开的后关闭,后打开的先关闭。
缓冲输出输入流需要和文件输出输入流结合使用.
System.currentTimeMillis();方法返回当前系统时间.所用时间=当前时间 - 基准时间(1970.1.1)的长整型时间差。
字符流:字符输入流Reader、字符输出流Writer。
在这里插入图片描述
在这里插入图片描述
读写的都是字符形式的数据
字节字符转换流:InputStreamReader、OutputStreamWriter。
字符缓冲输入输出流来提高写入读取速率.
对象序列化:步骤:1.创建一个类,实现Serializable接口.2.创建对象。3.将对象写入文件。4.从文件读取对象信息。
第三步用到的是writeObject()方法.第四步用到的是readObject()方法,但这个方法的返回值是Object,即所有类的父类,需要进行强制转换,并定义一个类去接收它。
对象输出流ObjectInputStream、对象输出流ObjectOutputStream
序列化:把Java对象转换为字节序列的过程。即写对象的过程。使其持久化(即将其保存在磁盘中)或通过网络发送到任何其他程序;
反序列化:把字节序列恢复为Java对象的过程。即读对象的过程。
Java提供的Serializable接口是一个空接口;如果一个类实现了Serializable接口,那么就代表这个类以及其子类是自动支持序列化和反序列化的!序列化时,只对对象的状态进行保存,而不管对象的方法。如果一个类没有实现Serializable接口,那么默认是不能被序列化的,除非使用其他办法。
同样也可以序列化其他类型,eg:写入:oss.writeBoolean(true);读取:System.out.println(ois.readBoolean());
反序列化时应当按照序列化的顺序读取,否则将会报EOFException异常,因为读取不会分辨特定的类型.
小知识点:
关键字instanceof保持着类型的概念,它指的是“你是这个类吗?”或者“你是这个类的派生类吗?”,而
getClass
得到的是确切的类型,并不考虑继承,它判断的是引用指向的对象的类型
1.obj.getClass():在java中通过反射,根据对象来获取类的对象的信息,obj.getClass()就是获取传入参数obj对象的类型信息。
2、Song.class:同样是通过反射,但这个是根据Song类获取对象信息。
if(obj.getClass()==Song.class) 就是判断要进行比较的obj的对象类型信息与Song的对象类型信息是否为同一类型。如果类型信息都是歌曲,则进行属性信息的比较.
对集合的增删改查功能中,查功能最基础,其他三种功能大部分能在查功能基础上完成,因此建议代码先实现查功能方法,增删改可调用查功能方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值