Java

引用 转载

https://blog.csdn.net/hao19980724/article/details/83792516

https://www.jianshu.com/p/49d2c3975c56

https://www.cnblogs.com/chenhuan821361335/p/8507636.html

https://www.cnblogs.com/hen-java/p/12621052.html

https://zhuanlan.zhihu.com/p/62117994

https://blog.csdn.net/lsxf_xin/article/details/79712537

https://www.cnblogs.com/junrong624/p/11596191.html

https://www.cnblogs.com/shuaiguoguo/p/8883862.html

Java

基础知识 FAQ

JVM、JRE和JDK的区别:

JVM (Java Virtual Machine): java虚拟机,用于保证java的跨平台的特性。
JRE (Java Runtime Environment): java的运行环境,包括jvm+java的核心类库。
JDK (Java Development Kit): java的开发工具,包括jre+开发工具
java语言是跨平台,jvm不是跨平台的。

classpath 是什么

Classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。

标示符命名规则:

由数字(0-9),大小写英文字母,以及_和$组成。
不能以数字开头。
不能使用关键字来自定义命名。

基本数据类型(4类8种):

 整数类型:byte、short、int、long
 浮点数类型:float、double
 字符类型:char
 布尔类型:boolean

(类型大小写注意,比如boolean是原始数据类型,Boolean是对应的封装对象)

类型位数最小值最大值默认值其他
byte8-128(-2^7)127(2^7-1)0有符号、二进制补码表示
short16-32768(-2^15)32767(2^15-1)0有符号、二进制补码表示
int32-2^312^31-10有符号、二进制补码表示
long64-2^632^63-10L(0l)有符号、二进制补码表示
float322^(-149)2^128-10.0f单精度、IEEE754标准
double642^(-1074)2^1024-10.0d双精度、IEEE754标准
char16\u0000(0)\uffff(65535)\u0000(0)单一的、Unicode字符
类型位数符号位指数位尾数位
float321823
double6411152

引用数据类型:

 类
 接口
 数组

类型转换

精度从高到低 double float long int short(char) byte
(1) 自动类型转换 将一个低精度 → 高精度
(2) 强制类型转换 将一个高精度 → 低精度(精度会下降)

java语言的三种技术架构

  • J2EE:企业版
    是为开发企业环境下的应用程序提供的一套解决方案。
    该技术体系中包含的技术如 Servlet、Jsp等,主要针对于Web应用程序开发。

  • J2SE:标准版
    是为开发普通桌面和商务应用程序提供的解决方案。
    该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。
    比如Java版的扫雷。

  • J2ME:小型版
    是为开发电子消费产品和嵌入式设备提供的解决方案。
    该技术体系主要应用于小型电子消费类产品,如手机中的应用程序等。

内存结构

  • 栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放。
  • 堆内存:数组和对象,通过new建立的实例都存放在堆内存中。
  • 方法区:静态成员、构造函数、常量池、线程池
  • 本地方法区:window系统占用
  • 寄存器:

成员变量和局部变量的区别 (重点)

(1)作用域
成员变量:针对整个类有效。
局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
(2)存储位置
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。
当方法调用完,或者语句结束后,就自动释放。
(3)初始值
成员变量:有默认初始值。
局部变量:没有默认初始值,使用前必须赋值。

匿名对象

(1)匿名对象就是没有名字的对象。是对象的一种简写形式。
(2)应用场景
A:只调用一次类中的方法。
B:可以作为实际参数在方法传递中使用

构造代码块:

(1) 作用:给对象进行初始化,对象一建立就执行,而且优先于构造函数执行
(2) 构造代码块和构造函数的区别:
构造代码块是给所有不同对象的共性进行统一初始化
构造函数是给对应的对象进行初始化

this关键字

(1)this关键字代表本类对象的一个引用,谁调用this所在的方法,this就代表谁
(2)this的使用场景
A:用于区分同名成员变量和局部变量;
B:在定义函数时,该函数内部要用到调用该函数的对象时,因为此时对象还没建立,故this代表此对象
C:构造函数间调用
这个时候,this(参数)必须作为第一条语句存在。

Person p = new Person();在内存中做了哪些事情。

(1)将Person.class文件加载进内存中。
(2)如果p定义在主方法中,那么,就会在栈空间开辟一个变量空间p。
(3)在堆内存给对象分配空间。
(4)对对象中的成员进行默认初始化。
(5)对对象中的成员进行显式初始化。
(6)调用构造代码块对对象进行初始化。(如果没有就不执行)
(7)调用构造方法对对象进行初始化。对象初始化完毕。
(8)将对象的内存地址赋值给p变量,让p变量指向该对象。

static关键字:

(1)静态的意思,用来修饰成员变量和成员函数
(2)静态的特点:
随着类的加载而加载
优先于对象存在
对所有对象共享
可以被类名直接调用
(3)静态的注意事项
A:静态方法只能访问静态成员
为什么:因为静态的内容是随着类的加载而加载,它是先进内存的。
B:静态方法中不能使用this,super关键字
C:主方法是静态的
public static void main(String[] args)
public:公共的意思,是最大权限修饰符。
static:由于jvm调用main方法的时候,没有创建对象。
只能通过类名调用。所以,main必须用static修饰。
void:由于main方法是被jvm调用,不需要返回值。用void修饰。
main:main是主要的意思,所以jvm采用了这个名字。是程序的入口。

   String[]:字符串数组
   args:数组名

   在运行的时候,通过java命令给args数组赋值。
   格式:java MainTest hello world itcast

静态变量和成员变量的区别

静态变量存储在方法区中的静态区。

静态的生命周期比较长,所以一般不推荐使用。

静态代码块

A:它只执行一次,它比main还先执行。
B:执行顺序
静态代码块 → 构造代码块 → 构造方法

制作API(次重点)

API(全拼):Application Program Interface 应用程序编程接口。
(1)类中的内容需要用文档注释。
(2)使用JDK\bin目录下的javadoc工具。
格式:javadoc -d 目录 -author -version ArrayTool.java

有符号数据的表示法(次重点)

原码,反码(原码取反),补码(反码+1)。

单例设计模式:

(1)设计模式:
解决某类问题行之有效的方法,是一种思想,是规律的总结
(2)用来保证某个类在内存中只有一个对象
(3)保证唯一性的思想及步骤
为了避免其他程序建立该类对象,先禁止其他程序建立该类对象,即将构造函数私有化
为了其他程序访问到该类对象,须在本类中创建一个该类私有对象
为了方便其他程序访问到该类对象,可对外提供一个公共访问方式

单例设计模式的两种方式

  • 第 1 种:懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

public class LazySingleton
{
    private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
    private LazySingleton(){}    //private 避免类在外部被实例化
    public static synchronized LazySingleton getInstance()
    {
        //getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

  • 第 2 种:饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

public class HungrySingleton
{
    private static final HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance()
    {
        return instance;
    }
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

Math类的使用(重点)

(1)数学操作类:该类没有构造函数,方法均为静态的
(2)掌握内容
A:成员变量
E:比任何其他值都更接近e(即自然对数的底数)的double值。
PI:比任何其他值都更接近pi(即圆的周长与直径之比)的double值。
B:成员方法
static double abs(double a)
返回 double 值的绝对值。返回绝对值
static double ceil(double a)
返回最小的(最接近负无穷大)double 值,该值大于等于参数,并等于某个整数。
static double floor(double a)
返回最大的(最接近正无穷大)double 值,该值小于等于参数,并等于某个整数。
max:返回两个值中较大的那个
min:返回两个值中较小的那个
static long round(double a) 返回最接近参数的 long。
static int round(float a) 返回最接近参数的 int。
static double random()
返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。
static double pow(double a, double b)
返回第一个参数的第二个参数次幂的值。
static double sqrt(double a)
返回正确舍入的 double 值的正平方根。

Random类的使用(重点)

(1)产生随机数的类
(2)掌握内容
A:构造方法
Random() 创建一个新的随机数生成器。
Random(long seed) 使用单个 long 种子创建一个新的随机数生成器。
B:成员方法
int nextInt() 返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
int nextInt(int n) 返回一个伪随机数,它是取自此随机数生成器序列的、
在 0(包括)和指定值(不包括)之间均匀分布的 int 值。

Scanner类的使用

(1)可以获取从键盘的输入数据
(2)掌握内容
构造方法:
Scanner(InputStream source) 构造一个新的 Scanner,它生成的值是从指定的输入流扫描的。
如:Scanner sc = new Scanner(System.in);
方法摘要
sc.nextInt();获取整型数据
sc.nextLine();获取字符串数据

继承(重点)

(1)把很多类的相同特征和行为进行抽取,用一个类来描述。让多个类和这个类产生一个关系。
这样的话,多个类就可以省略很多代码。这个关系就是继承。java中用extends关键字表示。
(2)继承的体系结构
A:多个具体的对象,不断的向上抽取共享的内容,最终形成了一个体系。这个体系叫做继承体系。
B:继承体系的学习和使用原则
学习顶层的内容。因为他是整个体系的共性内容。
创建子类使用。也就是使用底层的具体对象。
(3)继承的特点:
A:java中只能单继承,没有多继承。
B:java可以有多重(层)继承。
(4)继承的好处:
继承的出现提高了代码的复用性。
继承的出现让类与类之间产生了关系,提供了多态的前提。
(5)子父类中的成员关系
A:成员变量
在子类方法中使用一个变量时:
首先,在方法的局部变量中找这个变量,有则使用。
否则,在本类中找成员变量,有则使用。
否则,在父类中找成员变量,有则使用。
否则,报错。
B:成员方法
用子类对象使用一个方法时。
首先,在子类中找这个方法,有则使用。
否则,在父类中找这个方法,有则使用。
否则,报错。

 重写和重载的区别?
   重载:在同一类中。方法名相同,参数列表不同。重载可以改变返回类型。
   重写:在不同类中(子父类中)。
      方法声明相同(返回类型,方法名,参数列表均相同)。
 重写需要注意:
   子类方法的访问权限要大于等于父类方法的访问权限。
   静态只能重写静态。但是这种情况一般不会出现。

 构造方法
   子类的实例化过程
     *子类创建对象时,会先去创建父类的对象。
       默认是去调用父类的无参构造方法。
     *子类构造方法中,第一行默认是super()
     *为什么子类中第一行会默认有super()
       因为他继承父类的成员使用,使用前这些成员必须初始化,
       而他们是父类的成员,所以,必须通过父类进行初始化。
       所以,会先创建一个父类的对象。
   当父类没有无参构造方法时
     必须使用this或者super调用其他的构造方法。

(6)this和super的区别
this:代表本类对象的引用。
super:代表父类的存储空间。

final关键字(重点)

(1)最终的意思,可以用于修饰类,方法,变量。
(2)final修饰的类不能被继承。
final修饰的方法不能被重写。
final修饰的变量是一个常量。只能被赋值一次。
内部类只能访问被final修饰的局部变量。

为什么匿名内部类和局部内部类只能访问被final修饰的局部变量

根本原因是内部类的生命周期可能会超过局部变量的生命周期。局部变量的生命周期是当调用方法f()的时候,java虚拟机会将局部变量压入栈中(即局部变量产生),当方法f()运行结束后,java虚拟机就会将局部变量出栈(即局部变量销毁);但此时,内部类对象可能还存在,如果内部类对象此时去访问局部变量,那么就会出问题。解决办法就是把匿名内部类或局部内部类要访问的局部变量复制一份作为内部类对象的成员变量;这样当局部变量在栈中销毁以后,内部类还能访问局部变量。但是这样做会有一个问题,那就是必须时时刻刻保证复制的那一份局部变量的值和原来的局部变量的值相同,但是这样做是很困难的;于是,java中干脆就不允许内部类要访问的局部变量的值发生改变,也就是将这些局部变量用final修饰。(用final修饰的局部变量的值不能改变,只能是初始值。)

抽象类(重点)

(1)多个类有相同的方法声明,但是方法体不一样。这个时候,我们考虑把方法声明进行抽取。
让子类继承后,自己去实现方法体。没有方法体的方法,我们需要用抽象标志下。
抽象的关键字是:abstract。
(2)抽象类:
该方法称为抽象方法,包含抽象方法的类就是抽象类。
(3)抽象类的特点:
A:抽象类和抽象方法都要用abstract进行修饰
B:抽象类不能被实例化
C:抽象类中不一定有抽象方法,但是,有抽象方法的类一定是抽象类。
(4)抽象类中数据的特点
A:成员变量
抽象类中可以有变量,也可以有常量。
B:成员方法
抽象类中可以有抽象方法,也可以有非抽象方法。
C:构造方法
抽象类是一个类,所以,它有构造方法。
虽然本身不能实例化。但是可以给子类实例化使用。
(5)抽象类中的问题
A:抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
抽象类有构造方法。
抽象类不能被实例化。
抽象类中的构造方法供子类实例化调用。
B:抽象关键字abstract不可以和哪些关键字共存?
private:
私有内容子类继承不到,所以,不能重写。
但是abstract修饰的方法,要求被重写。两者冲突。
final
final修饰的方法不能被重写。
而abstract修饰的方法,要求被重写。两者冲突。
static
假如一个抽象方法能通过static修饰,那么这个方法,就可以直接通过类名调用。
而抽象方法是没有方法体的,这样的调用无意义。所以,不能用static修饰。
C:抽象类中可不可以没有抽象方法?如果可以,这样的类有什么用吗?
抽象类可以没有抽象方法。
抽象类中没有抽象方法的作用,只是为了不让别的类建立该抽象类对象。这个在awt中有体现。

接口interface

(1)当一个类中的方法都是抽象的时候,java提供了另一种表示方式,叫接口。
用interface关键字表示。类与接口关系用implements表示。
(2)接口的成员特点
A:成员变量
是常量,默认修饰 public static final
B:成员方法
都是抽象的,默认修饰 public abstract
(3)关系
A:类与类的关系
是继承关系。类与类只能单继承,可以多重继承。
B:类和接口的关系
是实现关系。类可以多实现接口。
类在继承一个类的同时,可以实现多个接口。
C:接口和接口的关系
是继承关系。接口可以多继承接口。
(4)接口的特点
A:是对外暴露的规则
B:是功能的扩展
C:接口的出现降低耦合性。
耦合(类与类之间的关系)
内聚(类完成功能的能力)
编程规范:低耦合,高内聚。
D:接口可以多实现。如:CPU和主板、笔记本的USB插口、插座
(5)接口和抽象类的区别
A:抽象类只能被单继承
接口可以多实现,接口的出现避免了多继承的局限性。
B:抽象类中的数据特点:
成员变量:可以是变量,也可以是常量
成员方法:可以是抽象方法,也可以是非抽象方法
构造方法:有构造方法
接口中的数据特点:
成员变量:是常量。默认修饰 public static final
成员方法:都是抽象方法。都有默认修饰 public abstract
构造方法:没有构造方法
C:抽象类中定义的是继承体系中的共性功能。
接口中定义的是继承体系中的扩展功能。
D:抽象类被继承是"is a"关系:xx是yy的一种
接口被实现是"like a"关系:xx像yy的一种

Object类

(1)是所有类的根类,超类。
java中提供的类以及我们自定义的类都直接或者间接的继承自Object类。
(2)Object类中的方法
A:void finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
B:Class getClass()
获取对象的字节码文件的描述类,后面再讲反射的时候还会在说这个类。
String name = s.getClass().getName();
C:int hashCode()
获取对象的哈希值。其实就是对象的内存地址值十进制表示
D:String toString()
返回对象的字符串表示。
表示格式:
getClass().getName()+"@"+Integer.toHexString(hashCode());

  一般我们输出对象名的时候,其实底层调用的就是该对象的toString()方法。
  这种返回没有意义,所以,我们会重写这个方法,显示类的成员变量信息。
 E:boolean equals(Object obj)
  用于比较两个对象的地址值是否相同。
  我们获取对象后,比较它的地址值意义不大。所以也会对这个方法进行重写。
  重写要完成什么功能,是根据需求定的。

(3)==和equals的用法:
A:==怎么用?
可以用于比较基本数据类型,比较的就是基本数据类型的值是否相等。
可以用于比较引用数据类型,比较的是对象的地址值是否相等。
B:equals怎么用?
equals只能用于比较引用数据类型的。
Object提供的equals是用于比较对象地址值是否相同。
自定义类中,如果重写了equals方法,那么就是按照你自己的需求来比较的。

内部类(次重点)

(1)把一个类定义在某个类中的,这个类就被称为内部类,内置类,嵌套类。
(2)访问特点:
A:内部类可以直接访问外部类中的成员,因为内部类持有外部类的引用,
格式为:外部类名.this
B:外部类要想访问内部类的成员,必须创建对象访问。
(3)内部类的访问格式:
A:当内部类定义在外部类的成员位置,而且非私有,则可以在其他外部类中直接建立内部类对象
格式:外部类名.内部类名 变量名 = new 外部类对象.内部类对象
如:Outer.Inner in = new Outer().new Inner()
B:当内部类在外部类成员位置,且被static修饰时
外部其他类可直接访问静态内部类的非静态成员
格式:new 外部类名.内部类名().内部类成员
如:new Outer.Inner().function();

外部其他类可直接访问静态内部类的静态成员
格式:new 外部类名.内部类名.内部类成员
如:new Outer.Inner.function();
(4)什么使用时候内部类呢?
假如有A类和B类,A类想直接访问B类的成员,B类访问A类成员的时候,
需要创建A类对象进行访问,这个时候,就可以把A类定义为B类的内部类。
(5)内部类的位置
A:成员位置
可以被private修饰(Body,Heart)
可以被static修饰。(它访问的外部类的成员必须是静态的)
B:局部位置
可以直接访问外部类中的成员,因为还持有外部类的持用
也可以直接访问局部成员,但是局部成员要用final修饰。
注意:局部内部类不能用private和static修饰
(6)通过class文件我们就可以区分是否带有内部类,以及内部类的位置
Outer$Inner:成员内部类
Outer$1Inner:局部内部类

匿名内部类(局部内部类的简写) (重点)

(1)前提:继承一个类或者实现一个接口
(注意不要弄混匿名内部类的前提和多态的前提)
(2)格式:
new 父类名或者接口名()
{
重写父类方法或者实现接口中的方法。
也可以自定义其他方法。
};
(3)什么时候定义匿名内部类?
匿名内部类只是为了简化书写,匿名内部类有局限,通常定义匿名内部类时,该类方法不超过3个
(4)匿名内部类的好处和弊端:
好处:简化代码书写
弊端:
不能直接调用自己的特有方法
不能执行强转换动作
如果该类里面方法较多,不允许使用匿名内部类

java 异常体系

5982616-4ab25f2cfc5ca7b8.jpeg (C:\Users\windows\Desktop\Project\Markdown\img\java exception.jpg)

  • Throwable 类是 Java 语言中所有错误或异常的超类。
  • 只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。
  • Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。
  • 最后,它还可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。此 cause 设施在 1.4 版本中首次出现。它也称为异常链 设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。

1.1 Error

  • Error 是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获的严重问题
  • 大多数这样的错误都是异常条件。虽然 ThreadDeath 错误是一个“正规”的条件,但它也是 Error 的子类,因为大多数应用程序都不应该试图捕获它。
  • 在执行该方法期间,无需在其 throws 子句中声明可能抛出但是未能捕获的 Error 的任何子类,因为这些错误可能是再也不会发生的异常条件。
  • Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。

1.2 Exception

  • Exception 异常主要分为两类
    • 一类是 IOException(I/O 输入输出异常),其中 IOException 及其子类异常又被称作「受查异常」
    • 另一类是 RuntimeException(运行时异常),RuntimeException 被称作「非受查异常」。
  • 受查异常就是指,编译器在编译期间要求必须得到处理的那些异常,你必须在编译期处理了。

1.2.1 常见的非检查性异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQwIHWh5-1596473477278)(C:\Users\windows\Desktop\Project\Markdown\img\5982616-97fef7461bed33c6.webp)]

1.2.2 常见的检查性异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E5TmLjjE-1596473477281)(C:\Users\windows\Desktop\Project\Markdown\img\5982616-ad31cf5b8e2cbb36.webp)]

2 自定义异常类型

Java 的异常机制中所定义的所有异常不可能预见所有可能出现的错误,某些特定的情境下,则需要我们自定义异常类型来向上报告某些错误信息。

  • 在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
    • 所有异常都必须是 Throwable 的子类。
    • 如果希望写一个检查性异常类,则需要继承 Exception 类。
    • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

3 异常的处理方式

3.1 try…catch关键字

  • 使用 try 和 catch 关键字可以捕获异常。
  • try/catch 代码块放在异常可能发生的地方。

try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:

try {
   // 程序代码
} catch(ExceptionName e1) {
   //Catch 块
}
  • Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
  • 一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获
  • 多重捕获块的语法如下所示:
try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}

3.2 throws/throw 关键字

  • 如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
  • 下面方法的声明抛出一个 RemoteException 异常:
public class className {
  public void deposit(double amount) throws RemoteException {
    // Method implementation
    throw new RemoteException();
  }
  //Remainder of class definition
}

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

3.3 finally关键字

  • finally 关键字用来创建在 try 代码块后面执行的代码块。
  • 无论是否发生异常,finally 代码块中的代码总会被执行。在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
  • finally 代码块出现在 catch 代码块最后,语法如下:
try{
  // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}finally{
  // 程序代码
}

Java中String,StringBuild,StringBuffer三者的区别

1,首先说说运行速度,或者执行速度,三者的速度快慢大概为:StringBuild>StringBuffer> String .

String 最常见,但最慢的原因:

String是字符串的常量,StringBuild和StringBuffer是字符串变量,String一旦创建就不可更改,新的String其实是新的常量,String只能被创建不能被修改,StringBuild和StringBuffer是可更改的,下面以代为为例:

1 String str="abc";
2 Sys.out.println(str);
3 str=str+"de";
4 sys.out.println(str);

运行这段代码会先输出abc,在输出abcde,看起来这个str对象的值被改变了,其实是假象,第二个是创建了新的对象,两个对象不一样, 在jvm中代码是这样处理的,首先创建一个String对象str并把abc赋值给str,然后在第三行代码中,jvm其实又创建了一个新的对象也叫str,并把原来的str的值赋值和“de”赋值给新的str,而原来的str会被垃圾回收机制(GC)给回收掉了,所以str并没有被修改,也就是String一旦被创建后就不可更改,所以,Java中对String对象进行的操作实际上是一个不断地创建新的对象并且将旧的对象回收的过程,所以实行速度很慢。

而StringBuild和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不惊醒创建和回收操作,所以速度要比String快很多。

另外有时候我们会这样对字符串进行赋值

1 String str="abc"+"de";
2 Stringbuilder stringBuilder=new StringBuilder().append("abc")+append("de");
3 Sys.out.println(str);
4 Sys.out.println(stringBuilder);

这样输出结果同样是“abcde”和“abcde”,但是String的速度比StringBuild的执行速度快得多,因为第一行中的操作和String str=“abcde”;是完全一样,第一行只是一次创建对象并赋值, 而第二行是一次创建并且多次更改值,而如果写成下面这种形式

1 String str1="abc";
2 String str2="de";
3 String str=str1+str2;
4 Sys.out.println(str);

那么jvm就像上面所说的那样,不断的创建对象,回收对象来进行这个操作了,速度就会很慢。

2,再来说线程安全

在线程上,StringBuilder是线程不安全的,StringBuffer是线程安全的

如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuild的方法没有关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作时多线程,那么就要用StringBuffer,但是在单线程的情况下,还是建议适用速度比较快的StringBuild。

3,总结一下

String:适用于少量的字符串操作的情况,即创建复制后,修改的比较少的情况

StringBuffer:适用于单线程下字符串缓冲区进行大量操作的情况

StringBuilder:适用于多线程下字符串缓冲区进行大量操作的情况

java 基本数据类型对象包装类

定义

为了方便操作基本数据类型值,Java将其封装成了对象,在对象中定义了属性和行为。用于描述该对象的类就称为基本数据类型对象包装类。

基本数据类型对象包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

作用

用于基本数据类型和字符串类型之间做转换。

基本数据类型转成字符串

  • 基本数据类型 + “”
    String num = 1 + "a";
  • 基本数据类型.toString(基本数据类型值);
    Integer.toString(34);// 将34整数变成“34”

字符串转成基本数据类型

  1. 使用包装类中的静态方法:xxx parseXxx(“xxx类型的字符串”)。
    • 只有Character没有parse方法,本身就是封装了字符,无须转换
int a = Integer.parseInt("123");
double b = Double.parseDouble("12.23");
boolean b = Boolean.parseBoolean("true");
  1. 如果字符串被xxx包装类进行对象的封装,使用xxxValue()。
/*格式
Xxx i = new Xxx(String);
xxx num = i.xxxValue();
*/
Integer i = new Integer(String);
int num = i.intValue();

扩展:自动装箱与拆箱

JDK1.5版本以后出现的新特性。

  • **自动装箱:**将一个基本数据类型的值赋给其所对应的包装类类型。
  • **自动拆箱:**将一个基本数据类型包装类类型的值赋给其所对应的基本数据类型。
Integer x = 4;//自动装箱。=new Integer(4)
x = x/* x.intValue() */ + 2;//x+2:x 进行自动拆箱,变成int类型后和2进行加法运算,再将和进行装箱赋给x。

对于新特性,当某一个数值存在于byte范围内时,java不会再开辟新的空间。

class Demo {
    public static void main(String[] args) {

        Integer m = 128;
        Integer n = 128;
        System.out.println("m==n:" + (m == n));//m==n:false
        Integer a = 127;
        Integer b = 127;
        System.out.println("a==b:" + (a == b));//a==b:true。因为a和b指向了同一个Integer对象。
    }
}

集合框架

(1)为什么出现集合类?
面向对象对事物的体现都是以对象的形式,为了方便对多个对象的操作,就对对象进行存储。
集合就是存储对象最常用的一种方式.
(2)数组和集合都是容器,两者有何不同?
**数组长度固定,而集合长度是可变的
**数组值可以存储对象,还可以存储基本数据类型;而集合只能存储对象
数组存储数据类型是固定的,而集合存储的数据类型不固定
(3)集合类的特点:
集合只能存储对象
集合的长度是可变的
集合可以存储不同类型的对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Us3Nheu-1596473477284)(C:\Users\windows\Desktop\Project\Markdown\img\java 集合框架.jpg)]

JDK1.2 版本后,Java完整的提供了类集合的概念。封装了一组强大的、非常方便的集合框架API,让我们在开发中大大提高了效率。

  • 集合中分为三大接口:
  • Collection
  • Map
  • Iterator

集合框架中的接口和类在 java.uitl 包中


  • Collection接口

Collection 层次结构中的根接口。Collection表示一组对象,这些对象也称为collection的元素。一些collection允许有重复的元素,一些不允许。一些是有序的,一些事无序的。JDK不提供此接口的任何直接实现。它提供更具体的子接口(Set、List)实现。此接口通常用来传递collection,并在需要最大普遍性的地方操作这些collection。

接口定义

public interface Collection<E> extends Iterable<E>
    1. List接口

有序的collection(序列)。此接口的用户可以对列表中每个元素的插入位置进行精确的控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

(1)接口定义

public interface List<E>extendsCollection<E>

(2)特点

  1. 有序的
  2. 允许重复
  3. 允许有多个 null 元素
  4. 具体的实现类:ArrayListVectorLinkedList

(3)如何选择:

  1. 安全性 Vector (ArrayList)
  2. 频繁插入、删除操作 LinkedList
  3. 是否遍历 ArrayList
  • ArrayList
  • 实现原理 采用动态数组,默认构造方法创建了一个空数组
  • 第一次添加元素扩充容量为10 ,之后的扩充算法 原 + 原>>1
  • 不适合进行删除操作或插入操作
  • 为了防止数组动态扩充次数太多,建议创建时,给定初始容量。
  • 线程不安全的
  • JDK 1.2 开始

示例

 List<String> list = new ArrayList<>();
 list.add("ArrayList");
 list.add("Test");
 int size = list.size(); 
 for (int i = 0; i < size; i++) {
     System.out.println(list.get(i));
 }
 System.out.println(list.remove("ArrayList"));
 System.out.println(list.contains("ArrayList"));
  • Vector
  • 实现原理,采用动态数组实现。默认构造方法创建了一个大小为10 的对象数组
  • 扩充的算法 增量为0时,扩充为 原*2 增量大于0,扩充为 原+增量
  • 不适合进行删除操作或插入操作
  • 为了防止数组动态扩充次数太多,建议创建时,给定初始容量。
  • 线程安全 适合在多线程中使用 在单线程中效率较低

示例

 List<String> list = new Vector<>();
 list.add("Vector");
 list.add("Test");
 int size = list.size();
 for (int i = 0; i < size; i++) {
 System.out.println(list.get(i));
 }
 System.out.println(list.remove("Vector"));
 System.out.println(list.contains("Test"));
  • LinkedList
  • 实现原理,采用 双向链表结构 实现
  • 适合插入,删除操作 性能高

示例

 LinkedList<String> linkedList = new LinkedList<>();
 linkedList.add("LinkedList");
 linkedList.add("Test");
 int size = linkedList.size();
 for (int i = 0; i < size; i++) {
 System.out.println(linkedList.get(i));
}
    1. Set接口

一个不包含重复元素的collection。更确切的讲,set不包含满足e1.equals(e2) 的元素对e1e2,并且最多包含一个 null 元素。正如其名所暗示的,接口模仿了数学上的set抽象。

(1)接口定义

public interface Set<E> extends Collection<E>

(2)特点

  1. 无序的(不保证顺序)
  2. 不允许有重复元素
  3. 具体实现类有:HashSetTreeSetLinkedHashSet

(3)如何选择:

  1. 要排序:选择 TreeSet
  2. 不需要排序也不需要保证顺序:选择 HashSet
  3. 不需要排序但需要保证排序:选择 LinkedHashSet
  • HashSet

类实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序。特别是它不保证该顺序恒久不变。允许使用 null 元素

接口定义

public class HashSet<E> extends AbstractSet<E> implements Set<E>,Cloneable,Serializable、
  • 实现原理 基于哈希表(HashMap) 实现 (数组 + 链表)
  • 不允许重复,可以有一个Null元素
  • 不保证顺序恒久不变
  • 添加元素时 把元素作为HashMap的key存储 value使用一个固定的object对象补充。
  • 自定义对象中如果认为属性相同为 相同对象 就需要重写 equals() 和 hashCode()
  • 如何把对象存储到哈希表中: 先计算对象的hashCode值,再对数组的长度取余 来决定存储的数组下标。
  • 解决hashSet中的重复值:判断两个对象是否相同 , 先判断 两个对象的hashCode是否相同。(hashCode相同不一定是同一个对象)如果hashCode不同 那么一定不相同。 如果hashCode相同,还要进行equals判断,equals相同,则相同,否则不同。
 Cat c1 = new Cat("Tom", 11);
 Cat c2 = new Cat("Tom2", 10);
 Cat c3 = new Cat("Tom", 11);
 Set<Cat> set2 = new HashSet<>();
 set2.add(c1);
 set2.add(c2);
 set2.add(c3);
 System.out.println("set2的长度:"+set2.size());
 for(Cat cat:set2) {
 System.out.println(cat+"--"+cat.hashCode());
}
  • TreeSet

基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序。具体取决于你使用的构造方法

接口定义

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>,Cloneable,Serializable
  • 有序的
  • 基于TreeMap (二叉树结构)实现 对象需要比较大小 通过比较器实现。
  • 对象比较器还可以去重。
  • 如果自定义的类,没有实现比较器结构将无法add到TreeSet集合中。
 //如果自定义的类,没有实现比较器结构将无法add到TreeSet集合中
 //TreeSet<Cat> treeSet =new TreeSet<Cat>();
 TreeSet<Cat> treeSet =new TreeSet<Cat>(new CatComparable());
 Cat c1 = new Cat("Tom", 11);
 Cat c2 = new Cat("Tom2", 11);
 //Cat c2 = new Cat("Tom2", 10);
 Cat c3 = new Cat("Tom", 18);
 treeSet.add(c1);
 treeSet.add(c2);
 treeSet.add(c3);
 System.out.println("treeSet的长度为:"+treeSet.size());
 for(Cat cat:treeSet) {
 System.out.println(cat);
 }
  • LinkedHashSet

具有可预知迭代顺序的Set接口,哈希表和链表实现。此实现与HashSet不同之处在于,LinkedHashset维护着一个运行于所有条目的双重链表。此链表定义了迭代顺序。即按照将元素插入顺序进行迭代。注意插入顺序不受再set中重新插入的元素的影响。(如果再s.contains(e)返回true后立即调用s.add(e), 则元素e会呗重新插入到set 中。

接口定义

public class LinkedHashSet<E> extends HashSet<E> implements Set<E>,Cloneable , Serializable
 LinkedHashSet<Cat> linkedHashSet = new LinkedHashSet<>();
 Cat c1 = new Cat("Tom", 1);
 Cat c2 = new Cat("Tom2", 3);
 Cat c3 = new Cat("Tom", 2);
 linkedHashSet.add(c1);
 linkedHashSet.add(c2);
 linkedHashSet.add(c3);
 System.out.println("linkedHashSet的长度为:"+linkedHashSet.size());
 for(Cat cat: linkedHashSet) {
 System.out.println(cat);
 }
  • Iterator 接口

1.集合输出

遍历集合的方式

  1. Iterator
  2. ListIterator
  3. Enumeration
  4. foreach

System

(1)描述系统信息的类
(2)该类没有构造方法,该类的方法和属性都是静态的
(3)字段摘要:
static InputStream in “标准”输入流。
static PrintStream out “标准”输出流。
(4)方法摘要:
static void exit(int status) 终止当前正在运行的 Java 虚拟机。
static void gc() 运行垃圾回收器。
static Properties getProperties() 确定当前的系统属性
static String getProperty(String key) 获取指定键指示的系统属性。
static String getProperty(String key, String def) 获取用指定键描述的系统属性。
static void setIn(InputStream in) 重新分配“标准”输入流。
static void setOut(PrintStream out) 重新分配“标准”输出流。
static void setProperties(Properties props) 将系统属性设置为 Properties 参数。
static String setProperty(String key, String value) 设置指定键指示的系统属性。

Runtime

(1)每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
可以通过 getRuntime 方法获取当前运行时。 应用程序不能创建自己的 Runtime 类实例。
(2)该类没有构造函数,也就是它不能直接创建对象,但是它里里面的方法又不是静态的
,故它一定有一个方法返回本类对象
(3)故该类是单例设计模式,保证在内存中只有一个对象
(4)方法摘要:
Process exec(String command) 在单独的进程中执行指定的字符串命令
void gc() 运行垃圾回收器。
static Runtime getRuntime() 返回与当前 Java 应用程序相关的运行时对象
void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的 Java 虚拟机

JDK1.5新特性

  • 自动装箱与拆箱:

自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。

自动拆箱的过程:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。

自动装箱,只需将该值赋给一个类型包装器引用,java会自动创建一个对象。

自动拆箱,只需将该对象值赋给一个基本类型即可。

java——类的包装器

类型包装器有:Double,Float,Long,Integer,Short,Character和Boolean

  • 枚举

把集合里的对象元素一个一个提取出来。枚举类型使代码更具可读性,理解清晰,易于维护。枚举类型是强类型的,从而保证了系统安全性。而以类的静态字段实现的类似替代模型,不具有枚举的简单性和类型安全性。

简单的用法:JavaEnum简单的用法一般用于代表一组常用常量,可用来代表一类相同类型的常量值。

复杂用法:Java为枚举类型提供了一些内置的方法,同事枚举常量还可以有自己的方法。可以很方便的遍历枚举对象。

  • 静态导入

通过使用 import static,就可以不用指定 Constants 类名而直接使用静态成员,包括静态方法。

import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out。

  • 可变参数(Varargs)

可变参数的简单语法格式为:

methodName([argumentList], dataType… argumentName);

  • 内省(Introspector)

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新 的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter /setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。

一 般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来 调用这些方法。

  • 泛型(Generic)

C++ 通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功能。一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制得类型转换。猛虎引入了泛型,它允许指定集合里元素的类型,这样你可以得到强类型在编译时刻进行类型检查的好处。

  • For-Each循环

For-Each循环得加入简化了集合的遍历。假设我们要遍历一个集合对其中的元素进行一些处理。

JDK1.6新特性

  • Desktop类和SystemTray类

在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray。

前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序.

  • 使用JAXB2来实现对象与XML之间的映射

JAXB是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。

我们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。

实际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。除了JAXB之外,我们还可以通过XMLBeans和Castor等来实现同样的功能。

  • 理解StAX

StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一种处理XML文档的API。

StAX 的来历 :在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).

由 于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX,所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版本). JDK6里面JAXP的版本就是1.4. 。

StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX通过提供一种基于事件迭代器(Iterator)的API让 程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件,然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;

SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM 采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。

  • 使用Compiler API

现在我们可以用JDK6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。

这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去 做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。

  • 轻量级Http Server API

JDK6 提供了一个简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在 这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.

  • 插入式注解处理API(Pluggable Annotation Processing API)

插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)

实 际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.

JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列.

JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,我们想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试方法。

  • 用Console开发控制台程序

JDK6 中提供了java.io.Console 类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但我们不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.

  • 对脚本语言的支持如: ruby, groovy, javascript

  • Common Annotations

Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.

随 着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.

下面列举出Common Annotations 1.0里面的10个Annotations Common Annotations

Annotation Retention Target Description

Generated Source ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用于标注生成的源代码

Resource Runtime TYPE, METHOD, FIELD 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式

Resources Runtime TYPE 同时标注多个外部依赖,容器会把所有这些外部依赖注入

PostConstruct Runtime METHOD 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct

PreDestroy Runtime METHOD 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的

PermitAll Runtime TYPE, METHOD 允许所有角色执行被标注的类或方法

DenyAll Runtime TYPE, METHOD 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行

DeclareRoles Runtime TYPE 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色

注意:

1.RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上

2.标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来

4.处理以上Annotations的工作是由Java EE容器来做, Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做

改动的地方最大的就是java GUI界面的显示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系统的Windows Aero视窗效果,而JDK1.5不支持!!!

你要在vista环境下编程的话最好装jdk6.0,否则它总是换到windows basic视窗效果.

JDK1.7新特性

  • switch中可以使用字串

String s = “test”;
switch (s) {
case “test” :
System.out.println(“test”);
case “test1” :
System.out.println(“test1”);
break ;
default :
System.out.println(“break”);
break ;
}

  • "<>"这个玩意儿的运用List tempList = new ArrayList<>(); 即泛型实例化类型自动推断。
public` `class` `JDK7GenericTest {
  ``public` `static` `void` `main(String[] args) {
   ``// Pre-JDK 7
   ``List lst1 = ``new` `ArrayList();
   ``// JDK 7 supports limited type inference for generic instance creation
   ``List lst2 = ``new` `ArrayList<>();
 
   ``lst1.add(``"Mon"``);
   ``lst1.add(``"Tue"``);
   ``lst2.add(``"Wed"``);
   ``lst2.add(``"Thu"``);
 
   ``for` `(String item: lst1) {
     ``System.out.println(item);
   ``}
 
   ``for` `(String item: lst2) {
     ``System.out.println(item);
   ``}
  ``}
}
  • 自定义自动关闭类

以下是jdk7 api中的接口,(不过注释太长,删掉了close()方法的一部分注释)

/**

 * A resource that must be closed when it is no longer needed.

 *

 * @author Josh Bloch


 * @since 1.7

 */

public interface AutoCloseable {

    /**

     * Closes this resource, relinquishing any underlying resources.

     * This method is invoked automatically on objects managed by the

     * {@code try}-with-resources statement.

     *

     */

    void close() throws Exception;

}

只要实现该接口,在该类对象销毁时自动调用close方法,你可以在close方法关闭你想关闭的资源,例子如下

class TryClose implements AutoCloseable {

 

 @Override

 public void close() throw Exception {

  System.out.println(" Custom close method …

                                         close resources ");

 }

}

//请看jdk自带类BufferedReader如何实现close方法(当然还有很多类似类型的类)

  public void close() throws IOException {

        synchronized (lock) {

            if (in == null)

                return;

            in.close();

            in = null;

            cb = null;

        }

    }
  • 新增一些取环境信息的工具方法
File System.getJavaIoTempDir() // IO临时文件夹

File System.getJavaHomeDir() // JRE的安装目录

File System.getUserHomeDir() // 当前用户目录

File System.getUserDir() // 启动java进程时所在的目录

.......
  • Boolean类型反转,空指针安全,参与位运算
Boolean Booleans.negate(Boolean booleanObj)

True => False , False => True, Null => Null

boolean Booleans.and(boolean[] array)

boolean Booleans.or(boolean[] array)

boolean Booleans.xor(boolean[] array)

boolean Booleans.and(Boolean[] array)

boolean Booleans.or(Boolean[] array)

boolean Booleans.xor(Boolean[] array)
  • 两个char间的equals
boolean Character.equalsIgnoreCase(char ch1, char ch2)
  • 安全的加减乘除
int Math.safeToInt(long value)

int Math.safeNegate(int value)

long Math.safeSubtract(long value1, int value2)

long Math.safeSubtract(long value1, long value2)

int Math.safeMultiply(int value1, int value2)

long Math.safeMultiply(long value1, int value2)

long Math.safeMultiply(long value1, long value2)

long Math.safeNegate(long value)

int Math.safeAdd(int value1, int value2)

long Math.safeAdd(long value1, int value2)

long Math.safeAdd(long value1, long value2)

int Math.safeSubtract(int value1, int value2)
  • 对Java集合(Collections)的增强支持

在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下:

以List、Set、Map集合容器为例:

//创建List接口对象


    List<String> list=new ArrayList<String>();

    list.add("item"); //用add()方法获取对象

    String Item=list.get(0); //用get()方法获取对象

    //创建Set接口对象

    Set<String> set=new HashSet<String>();

    set.add("item"); //用add()方法添加对象

    //创建Map接口对象

    Map<String,Integer> map=new HashMap<String,Integer>();

    map.put("key",1); //用put()方法添加对象

    int value=map.get("key");

在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下:

List<String> list=["item"]; //向List集合中添加元素

      String item=list[0]; //从List集合中获取元素

      Set<String> set={"item"}; //向Set集合对象中添加元素

      Map<String,Integer> map={"key":1}; //向Map集合中添加对象

      int value=map["key"]; //从Map集合中获取对象
  • 数值可加下划线
例如:int one_million = 1_000_000;
  • 支持二进制文字
例如:int binary = 0b1001_1001;
  • 简化了可变参数方法的调用

当程序员试图使用一个不可具体化的可变参数并调用一个varargs (可变)方法时,编辑器会生成一个“非安全操作”的警告。

  • 在try catch异常扑捉中,一个catch可以写多个异常类型,用"|"隔开

jdk7之前:

try {
   ......
} catch(ClassNotFoundException ex) {
   ex.printStackTrace();
} catch(SQLException ex) {
   ex.printStackTrace();
}

jdk7例子如下

try {
   ......
} catch(ClassNotFoundException|SQLException ex) {
   ex.printStackTrace();
}
  • jdk7之前,你必须用try{}finally{}在try内使用资源,在finally中关闭资源,不管try中的代码是否正常退出或者异常退出。jdk7之后,你可以不必要写finally语句来关闭资源,只要你在try()的括号内部定义要使用的资源

jdk7之前:

import java.io.*;
// Copy from one file to another file character by character.
// Pre-JDK 7 requires you to close the resources using a finally block.
public class FileCopyPreJDK7 {
   public static void main(String[] args) {
      BufferedReader in = null;
      BufferedWriter out = null;
      try {
         in  = new BufferedReader(new FileReader("in.txt"));
         out = new BufferedWriter(new FileWriter("out.txt"));
         int charRead;
         while ((charRead = in.read()) != -1) {
            System.out.printf("%c ", (char)charRead);
            out.write(charRead);
         }
      } catch (IOException ex) {
         ex.printStackTrace();
      } finally {            // always close the streams
         try {
            if (in != null) in.close();
            if (out != null) out.close();
         } catch (IOException ex) {
            ex.printStackTrace();
         }
      }
 
      try {
         in.read();   // Trigger IOException: Stream closed
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }
}

jdk7之后

import java.io.*;
// Copy from one file to another file character by character.
// JDK 7 has a try-with-resources statement, which ensures that
// each resource opened in try() is closed at the end of the statement.
public class FileCopyJDK7 {
   public static void main(String[] args) {
      try (BufferedReader in  = new BufferedReader(new FileReader("in.txt"));
           BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"))) {
         int charRead;
         while ((charRead = in.read()) != -1) {
            System.out.printf("%c ", (char)charRead);
            out.write(charRead);
         }
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }
}

JDK1.8新特性

  • default关键字

在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现,如下。

public interface NewCharacter {
    
    public void test1();
    
    public default void test2(){
        System.out.println("我是新特性1");
    }

}

那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?

其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。

public class NewCharacterImpl implements NewCharacter{

    @Override
    public void test1() {
        
    }
    
    public static void main(String[] args) {
        NewCharacter nca = new NewCharacterImpl();
        nca.test2();
    }

}

所以说这个default方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk的集合List里面增加了一个sort方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList等都需要对其添加实现,那么现在用default定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

  • Lambda 表达式

Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。

首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

简单的来说就是,函数也是一等公民了,在java里面一等公民有变量,对象,那么函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,看一下下面这个例子。

//这是常规的Collections的排序的写法,需要对接口方法重写
        public void test1(){
        List<String> list =Arrays.asList("aaa","fsa","ser","eere");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        for (String string : list) {
            System.out.println(string);
        }
    }
//这是带参数类型的Lambda的写法
        public void testLamda1(){
        List<String> list =Arrays.asList("aaa","fsa","ser","eere");
        Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
            return b.compareTo(a);
        }
        );
        for (String string : list) {
            System.out.println(string);
        }
    }
//这是不带参数的lambda的写法
        public void testLamda2(){
        List<String> list =Arrays.asList("aaa","fsa","ser","eere");
        Collections.sort(list, (a,b)->b.compareTo(a)
        );
        for (String string : list) {
            System.out.println(string);
        }

可以看到不带参数的写法一句话就搞定了排序的问题,所以引入lambda表达式的一个最直观的作用就是大大的简化了代码的开发,像其他一些编程语言Scala,Python等都是支持函数式的写法的。当然,不是所有的接口都可以通过这种方法来调用,只有函数式接口才行,jdk1.8里面定义了好多个函数式接口,我们也可以自己定义一个来调用,下面说一下什么是函数式接口。

  • 函数式接口

定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。

@FunctionalInterface
public interface MyLamda {
    
    public void test1(String y);

//这里如果继续加一个抽象方法便会报错
//    public void test1();
    
//default方法可以任意定义
    default String test2(){
        return "123";
    }
    
    default String test3(){
        return "123";
    }

//static方法也可以定义
    static void test4(){
        System.out.println("234");
    }

}

看一下这个接口的调用,符合lambda表达式的调用方法。

MyLamda m = y -> System.out.println("ss"+y);
  • 方法与构造函数引用

jdk1.8提供了另外一种调用方式::,当 你 需 要使用 方 法 引用时 , 目 标引用 放 在 分隔符::前 ,方法 的 名 称放在 后 面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法,如下示例。

//先定义一个函数式接口
@FunctionalInterface
public interface TestConverT<T, F> {
    F convert(T t);
}

测试如下,可以以::形式调用。

public void test(){
    TestConverT<String, Integer> t = Integer::valueOf;
    Integer i = t.convert("111");
    System.out.println(i);
}

此外,对于构造方法也可以这么调用。

//实体类User和它的构造方法
public class User {    
    private String name;
    
    private String sex;

    public User(String name, String sex) {
        super();
        this.name = name;
        this.sex = sex;
    }
}
//User工厂
public interface UserFactory {
    User get(String name, String sex);
}
//测试类
    UserFactory uf = User::new;
    User u = uf.get("ww", "man");

这里的User::new就是调用了User的构造方法,Java编译器会自动根据UserFactory.get方法的签名来选择合适的构造函数。

  • 局部变量限制

Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda。 Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final,或事实上是final。
  为什么局部变量有这些限制?
  (1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
  (2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。

final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
stringConverter.convert(2); 
  • Date Api更新

1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。

6.1 LocalDate/LocalTime/LocalDateTime

LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。

now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plus或minus方法可以增减日期或时间信息;

6.2TemporalAdjusters

这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。

6.3DateTimeFormatter

以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:

LocalDateTime dt = LocalDateTime.now();  
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");         
System.out.println(dtf.format(dt));

定义:流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

流的操作类型分为两种:

  • Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

  • Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

    在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

构造流的几种方式

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
  • Objects方法新特性
//比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);

//深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的对应下标值是否相同)
public static boolean deepEquals(Object a, Object b);

//返回对象的hashCode,若传入的为null,返回0
public static int hashCode(Object o);

//返回传入可变参数的所有值的hashCode的总和(这里说总和有点牵强,具体参考Arrays.hashCode()方法)
public static int hash(Object... values);

//返回对象的String表示,若传入null,返回null字符串
public static String toString(Object o)

//返回对象的String表示,若传入null,返回默认值nullDefault
public static String toString(Object o, String nullDefault)

//使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
public static <T> int compare(T a, T b, Comparator<? super T> c) 

//如果传入的obj为null抛出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj) 

//如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)

-----------------------------以下是jdk8新增方法---------------------------

//判断传入的obj是否为null,是返回true,否者返回false
public static boolean isNull(Object obj)

//判断传入的obj是否不为null,不为空返回true,为空返回false (和isNull()方法相反)
public static boolean nonNull(Object obj)

//如果传入的obj为null抛出NullPointerException并且使用参数messageSupplier指定错误信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)

下面是几个简单的实例:

package cn.cupcat.java8;

import org.junit.Test;

import java.util.Comparator;
import java.util.Objects;

/**
 * Created by xy on 2017/12/25.
 */
public class ObjectsTest {

    /**
     *  因为Objects类比较简单,所以只用这一个测试用例进行测试
     * */
    @Test
    public void equalsTest(){
        String str1 = "hello";
        String str2 = "hello";
        //传入对象
        //Objects.equals(str1, str2) ?  true
        boolean equals = Objects.equals(str1, str2);
        System.out.println("Objects.equals(str1, str2) ?  "+ equals);



    }
    @Test
    public void deepEqualsTest(){
        String str1 = "hello";
        String str2 = "hello";
        //传入对象
        boolean deepEquals = Objects.deepEquals(str1, str2);
        //Objects.deepEquals(str1, str2) ?  true
        System.out.println("Objects.deepEquals(str1, str2) ?  "+ deepEquals);
        int[] arr1 = {1,2};
        int[] arr2 = {1,2};
        //传入数组
        deepEquals = Objects.deepEquals(arr1, arr2);
        //Objects.deepEquals(arr1, arr2) ?  true
        System.out.println("Objects.deepEquals(arr1, arr2) ?  "+ deepEquals);
    }
    @Test
    public void hashCodeTest(){
        String str1 = "hello";

        //传入对象
        int hashCode = Objects.hashCode(str1);
        //Objects.hashCode(str1) ?  99162322
        System.out.println("Objects.hashCode(str1) ?  "+ hashCode);

        //传入null
        hashCode = Objects.hashCode(null);
        //Objects.hashCode(null) ?  0
        System.out.println("Objects.hashCode(null) ?  "+ hashCode);


    }

    @Test
    public void hashTest(){

        int  a  = 100;

        //传入对象
        int hashCode = Objects.hashCode(a);
        //Objects.hashCode(str1) ?  100
        System.out.println("Objects.hashCode(str1) ?  "+ hashCode);

        //输入数组
        int[] arr = {100,100};
        hashCode = Objects.hash(arr);
        //Objects.hashCode(arr) ?  1555093793
        System.out.println("Objects.hashCode(arr) ?  "+ hashCode);
    }


    @Test
    public void compareTest(){
        int a = 10;
        int b = 11;
        int compare = Objects.compare(a, b, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        // compare = -1
        System.out.println(" compare = "+ compare);

    }

    @Test
    public void requireNonNullTest(){
        String test = null;
        //java.lang.NullPointerException
       // String s = Objects.requireNonNull(test);

        //java.lang.NullPointerException: 这是空指针异常提示的信息
        //String s = Objects.requireNonNull(test, "这是空指针异常提示的信息");

        
        //java.lang.NullPointerException: 我是返回的异常信息
        String s = Objects.requireNonNull(test,()->"我是返回的异常信息");
    }
}

java io 流

  • 基本概念

IO:Java对数据的操作是通过流的方式,IO流用来处理设备之间的数据传输,上传文件和下载文件,Java用于操作流的对象都在IO包中。

  • IO流的分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cZUaHapr-1596473477286)(C:\Users\windows\Desktop\Project\Markdown\img\java io.png)]

  • 字节流

  • 字节流基类

  • InputStream

InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类。

 常用方法:

    // 从输入流中读取数据的下一个字节
    abstract int read()
    // 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中
    int read(byte[] b)
    // 将输入流中最多 len 个数据字节读入 byte 数组
    int read(byte[] b, int off, int len)


    // 跳过和丢弃此输入流中数据的 n个字节
    long skip(long n)

    // 关闭此输入流并释放与该流关联的所有系统资源
    void close()
  • OutputStream

OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。

 常用方法:
    // 将 b.length 个字节从指定的 byte 数组写入此输出流
    void write(byte[] b)
    // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
    void write(byte[] b, int off, int len)
    // 将指定的字节写入此输出流
    abstract void write(int b)

    // 关闭此输出流并释放与此流有关的所有系统资源
    void close()

    // 刷新此输出流并强制写出所有缓冲的输出字节
    void flush()
  • 字节文件操作流

  • FileInputStream

FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。

 构造方法:
    // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
    FileInputStream(File file)
    // 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定
    FileInputStream(String name)

 常用方法:覆盖和重写了父类的的常用方法。
        // 读取f盘下该文件f://hell/test.txt
        //构造方法1
        InputStream inputStream = new FileInputStream(new File("f://hello//test.txt"));
        int i = 0;
        //一次读取一个字节
        while ((i = inputStream.read()) != -1) {

            // System.out.print(i + " ");// 65 66 67 68
            //为什么会输出65 66 67 68?因为字符在底层存储的时候就是存储的数值。即字符对应的ASCII码。
            System.out.print((char) i + " ");// A B C D
        }
        //关闭IO流
        inputStream.close();
        // 读取f盘下该文件f://hell/test.txt
        //构造方法2
        InputStream inputStream2 = new FileInputStream("f://hello/test.txt");
        // 字节数组
        byte[] b = new byte[2];
        int i2 = 0;
        //  一次读取一个字节数组
        while ((i2 = inputStream2.read(b)) != -1) {

            System.out.print(new String(b, 0, i2) + " ");// AB CD
        }
        //关闭IO流
        inputStream2.close();

注: 一次读取一个字节数组,提高了操作效率,IO流使用完毕一定要关闭。

  • FileOutputStream

FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。

 构造方法:
    // 创建一个向指定File对象表示的文件中写入数据的文件输出流
    FileOutputStream(File file)
    // 创建一个向指定File对象表示的文件中写入数据的文件输出流
    FileOutputStream(File file, boolean append)
    // 创建一个向具有指定名称的文件中写入数据的输出文件流
    FileOutputStream(String name)
    // 创建一个向具有指定name的文件中写入数据的输出文件流
    FileOutputStream(String name, boolean append)

 常用方法:覆盖和重写了父类的的常用方法。
        OutputStream outputStream = new FileOutputStream(new File("test.txt"));
        // 写出数据
        outputStream.write("ABCD".getBytes());
        // 关闭IO流
        outputStream.close();

        // 内容追加写入
        OutputStream outputStream2 = new FileOutputStream("test.txt", true);
        // 输出换行符
        outputStream2.write("\r\n".getBytes());
        // 输出追加内容
        outputStream2.write("hello".getBytes());
        // 关闭IO流
        outputStream2.close();

注;输出的目的地文件不存在,则会自动创建,不指定盘符的话,默认创建在项目目录下;输出换行符时一定要写\r\n不能只写\n,因为不同文本编辑器对换行符的识别存在差异性。

  • 字节缓冲流(高效流)

  • BufferedInputStream

BufferedInputStream:字节缓冲输入流,提高了读取效率。

     构造方法:
     // 创建一个 BufferedInputStream并保存其参数,即输入流in,以便将来使用。
     BufferedInputStream(InputStream in)
     // 创建具有指定缓冲区大小的 BufferedInputStream并保存其参数,即输入流in以便将来使用
     BufferedInputStream(InputStream in, int size)
        InputStream in = new FileInputStream("test.txt");
        // 字节缓存流
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[] bs = new byte[20];
        int len = 0;
        while ((len = bis.read(bs)) != -1) {

            System.out.print(new String(bs, 0, len));
            // ABCD
            // hello
        }
        // 关闭流
        bis.close();
  • BufferedOutputStream

BufferedOutputStream:字节缓冲输出流,提高了写出效率。

     构造方法:
     // 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
     BufferedOutputStream(OutputStream out)
     // 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
     BufferedOutputStream(OutputStream out, int size)

     常用方法:
     // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流
     void write(byte[] b, int off, int len)
     // 将指定的字节写入此缓冲的输出流
     void write(int b)
     // 刷新此缓冲的输出流
     void flush()
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt", true));
        // 输出换行符
        bos.write("\r\n".getBytes());
        // 输出内容
        bos.write("Hello Android".getBytes());
        // 刷新此缓冲的输出流
        bos.flush();
        // 关闭流
        bos.close();
  • 字符流

  • 字符流基类

  • Reader

Reader:读取字符流的抽象类.

  常用方法:
    // 读取单个字符
    int read()
    // 将字符读入数组
    int read(char[] cbuf)
    // 将字符读入数组的某一部分
    abstract int read(char[] cbuf, int off, int len)
    // 跳过字符
    long skip(long n)

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

Writer:写入字符流的抽象类.

 常用方法:
    // 写入字符数组
     void write(char[] cbuf)
    // 写入字符数组的某一部分
    abstract void write(char[] cbuf, int off, int len)
    // 写入单个字符
    void write(int c)
    // 写入字符串
    void write(String str)
    // 写入字符串的某一部分
    void write(String str, int off, int len)

    // 将指定字符添加到此 writer
    Writer append(char c)
    // 将指定字符序列添加到此 writer
    Writer append(CharSequence csq)
    // 将指定字符序列的子序列添加到此 writer.Appendable
    Writer append(CharSequence csq, int start, int end)

    // 关闭此流,但要先刷新它
    abstract void close()
    // 刷新该流的缓冲
    abstract void flush()
  • 字符转换流

  • InputStreamReader

InputStreamReader:字节流转字符流,它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

 构造方法:
    // 创建一个使用默认字符集的 InputStreamReader
    InputStreamReader(InputStream in)
    // 创建使用给定字符集的 InputStreamReader
    InputStreamReader(InputStream in, Charset cs)
    // 创建使用给定字符集解码器的 InputStreamReader
    InputStreamReader(InputStream in, CharsetDecoder dec)
    // 创建使用指定字符集的 InputStreamReader
    InputStreamReader(InputStream in, String charsetName)
 特有方法:
    //返回此流使用的字符编码的名称 
    String getEncoding() 
        //使用默认编码        
        InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"));
        int len;
        while ((len = reader.read()) != -1) {
            System.out.print((char) len);//爱生活,爱Android

        }
        reader.close();

         //指定编码 
        InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"),"utf-8");
        int len;
        while ((len = reader.read()) != -1) {
            System.out.print((char) len);//????????Android
        }
        reader.close();

注:Eclipse默认使用GBK编码,test.txt文件所以是GBK编码,当指定utf-8编码时所以会乱码。

  • OutputStreamWriter

OutputStreamWriter:字节流转字符流。

 构造方法:
    // 创建使用默认字符编码的 OutputStreamWriter
    OutputStreamWriter(OutputStream out)
    // 创建使用给定字符集的 OutputStreamWriter
    OutputStreamWriter(OutputStream out, Charset cs)
    // 创建使用给定字符集编码器的 OutputStreamWriter
    OutputStreamWriter(OutputStream out, CharsetEncoder enc)
    // 创建使用指定字符集的 OutputStreamWriter
    OutputStreamWriter(OutputStream out, String charsetName)
 特有方法:
    //返回此流使用的字符编码的名称 
    String getEncoding() 
  • 字符缓冲流(高效流)

  • BufferedReader

BufferedReader:字符缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

 构造方法:
    // 创建一个使用默认大小输入缓冲区的缓冲字符输入流
    BufferedReader(Reader in)
    // 创建一个使用指定大小输入缓冲区的缓冲字符输入流
    BufferedReader(Reader in, int sz)
 特有方法:
    // 读取一个文本行
    String readLine()
        //生成字符缓冲流对象
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt")));
        String str;
        //一次性读取一行
        while ((str = reader.readLine()) != null) {
            System.out.println(str);// 爱生活,爱Android
        }

        //关闭流
        reader.close();
  • BufferedWriter

BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

 构造方法:
    // 创建一个使用默认大小输出缓冲区的缓冲字符输出流
    BufferedWriter(Writer out)
    // 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
    BufferedWriter(Writer out, int sz)
 特有方法:
    // 写入一个行分隔符
    void newLine() 
  • FileReader、FileWriter
 FileReader:InputStreamReader类的直接子类,用来读取字符文件的便捷类,使用默认字符编码。
 FileWriter:OutputStreamWriter类的直接子类,用来写入字符文件的便捷类,使用默认字符编码。
  • 高效流效率比对
读取f盘下的一个视频文件到项目中:文件大小29.5 MB
  • 读取方式一:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        int len;
        // 开始时间
        long begin = System.currentTimeMillis();
        // 一次读取一个字节
        while ((len = inputStream.read()) != -1) {
            outputStream.write(len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 213195
        //关闭流释放资源
        inputStream.close();
        outputStream.close();
  • 读取方式二:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        int len;
        byte[] bs = new byte[1024];
        // 开始时间
        long begin = System.currentTimeMillis();
        // 一次读取一个字节数组
        while ((len = inputStream.read(bs)) != -1) {
            outputStream.write(bs, 0, len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 281

        inputStream.close();
        outputStream.close();
  • 读取方式三:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        int len;
        byte[] bs = new byte[1024];
        // 开始时间
        long begin = System.currentTimeMillis();
        while ((len = bis.read(bs)) != -1) {
            bos.write(bs, 0, len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 78

        bis.close();
        bos.close();
  • BufferedWriter

BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

 构造方法:
    // 创建一个使用默认大小输出缓冲区的缓冲字符输出流
    BufferedWriter(Writer out)
    // 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
    BufferedWriter(Writer out, int sz)
 特有方法:
    // 写入一个行分隔符
    void newLine() 
  • FileReader、FileWriter
 FileReader:InputStreamReader类的直接子类,用来读取字符文件的便捷类,使用默认字符编码。
 FileWriter:OutputStreamWriter类的直接子类,用来写入字符文件的便捷类,使用默认字符编码。
  • 高效流效率比对
读取f盘下的一个视频文件到项目中:文件大小29.5 MB
  • 读取方式一:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        int len;
        // 开始时间
        long begin = System.currentTimeMillis();
        // 一次读取一个字节
        while ((len = inputStream.read()) != -1) {
            outputStream.write(len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 213195
        //关闭流释放资源
        inputStream.close();
        outputStream.close();
  • 读取方式二:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        int len;
        byte[] bs = new byte[1024];
        // 开始时间
        long begin = System.currentTimeMillis();
        // 一次读取一个字节数组
        while ((len = inputStream.read(bs)) != -1) {
            outputStream.write(bs, 0, len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 281

        inputStream.close();
        outputStream.close();
  • 读取方式三:
        FileInputStream inputStream = new FileInputStream("f://滑板//HEEL_FLIP.mp4");
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        FileOutputStream outputStream = new FileOutputStream("HEEL_FLIP.mp4");
        BufferedOutputStream bos = new BufferedOutputStream(outputStream);
        int len;
        byte[] bs = new byte[1024];
        // 开始时间
        long begin = System.currentTimeMillis();
        while ((len = bis.read(bs)) != -1) {
            bos.write(bs, 0, len);
        }
        // 用时毫秒
        System.out.println(System.currentTimeMillis() - begin);// 78

        bis.close();
        bos.close();

注:由此可以看出高效缓冲流读写速度是非常快的,建议使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值