前言 : 记录自己的学习历程
一.面向过程学习
1.基本数据类型
- 整数 long int byte short
- 浮点数 float double
- 布尔 boolen
- 字符 char
- 注意 : float数据需要F结尾,long数据L结尾
2.运算符
算数运算符
加+ 、减- 、乘* 、 /(取整) 、 %(取余)
关系运算符
等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)和小于等于(<=)
逻辑运算符
与(&)、或(|)、非(!)、异或(^)
短路运算符 ( && )( || )
&& || 与& | 运算结果相同 但效率高
- && 两边都为真才返回真,如果左边为假,则直接返回false,不再判断右边的表达式
- || 两边存在一个为真就返回真,如果左边为真,则直接返回true,不再判断右边的表达式
赋值运算符
等号(=)、加等于(+=)、减等于(-=)*=、 /=、 %=
自增自减
递增(++)、递减(--)
三目运算符
- 格式为: 关系表达式 ? 表达式1 : 表达式2
- 关系表达式为ture,执行表达式1,为false执行表达式2
两种数据转换方式
- 强制转换
- 格式: 目标数据类型 变量名 = (目标数据类型)被强转的数据
- 隐式转换(系统自动)
- 把取值范围小的转成取值范围大的
- 从小到大 byte short int long float double
- byte short char 三种类型的数据在运算时,都会直接先提升为int ,然后再进行运算
字符串拼接(+)
// 字符串拼接
// +的本质 用到了链式编程 如下 :
// 拼接后的字符串 = new StringBuilder().append(原字符串).append(需要拼接的字符串).toString()
// + 前后存在字符串,则前后拼接,返回一个新的字符串
System.out.println(1 + "abc" + 2); //1abc2
// 从左向右依次运算 第一个+前后都为数字,进行算数计算,之后再进行拼接操作
System.out.println(1 + 2 + "abc" + 2);//3abc2
// 如果时变量 则与变量内的值进行运算,与上面两条规则相同
int i = 1, j = 2;
System.out.println(i + "abc" + j);//1abc2
System.out.println(i + j + "abc" + j);//3abc2
//若存在字符,则运算其ASCII值
char c = 'a';
int c1 = c + 1;
System.out.println(c1);//98
3.流程控制
3.1判断
if (关系表达式1) {
语句体1;
} else if (关系表达式2) {
语句体2;
}
…
else {
语句体n+1;
}
3.2选择
switch (表达式) {
case 1:
语句体1;
break;
case 2:
语句体2;
break;
...
default:
语句体n+1;
break;
-case穿透
// -> 可省略 break;
// default可省略 位置可变换
System.out.println("请输入一个整数 范围1-10 : ");
int i2 = sc.nextInt();
switch (i2){
case 1,2,3-> System.out.println("输入值为1-3");
case 4,5,6-> System.out.println("输入值为4-6");
//default-> System.out.println("输入错误");
case 7,8,9,10-> System.out.println("输入值为7-10");
//default-> System.out.println("输入错误");
}
3.2循环
for (初始化语句;条件判断语句;条件控制语句) {
循环体语句;
}
while(条件判断语句){
循环体;
条件控制语句;
}
do{
循环体;
条件控制语句;
}while(条件判断语句);
//do--while先执行一次循环体的内容
// continue 表示:跳过本次循环,继续执行下次循环
// break 表示结束,跳出的意思,跳出循环
4.数组
4.1定义和创建
// 静态初始化
// 简化格式 : 数据类型 数组名[] = {v1,v2,v3 }
int[] ar1 = {1,2,3};
// 动态初始化
// 格式 : 数据类型[] 数组名 = new 数据类型[数组的长度];
int[] ar2 = new int[10];
4.2遍历
for(int i = 0; i < arr.length; i++){
//在循环的过程中,i依次表示数组中的每一个索引
System.out.println(arr[i]);//就可以把数组里面的每一个元素都获取出来,并打印在控制台上了。
}
4.3浅拷贝与深拷贝
- 深拷贝:拷贝后,修改原数组,不会影响到新数组;
- 浅拷贝:拷贝后,修改原数组,新数组也会相应的发生改变;
- 本质: 浅拷贝只是把地址值拷贝给另一个数组,他们指向的是同一块地址,而深拷贝则是new了一块地址,将原数组的值全部拷贝下来,是独立的一块地址
int[] ar1 = {1,2,3}; // 原数组
//浅拷贝
int[] ar2 = ar1; // 看下面两行代码结果,其地址值相同,指向同一块内存
System.out.println(ar2); //[I@10f87f48
System.out.println(ar1); //[I@10f87f48
//深拷贝
int[] ar3 = new int[ar1.length]; // 拷贝对象
for (int i = 0; i < ar3.length; i++) {
System.out.println(ar4[i]);
} // 0 0 0 int初始化值为0
method01(ar1,ar3); // 一个深拷贝的方法
for (int i = 0; i < ar3.length; i++) {
System.out.println(ar4[i]);
} // 1 2 3 拷贝成功
// 一个深拷贝的方法
public static void method01(int[] arr, int[] ans) {
for (int i = 0; i < arr.length; i++) {
ans[i] = arr[i]; // 原数组每一个值拷贝给新数组
}
}
5.方法
- 类似c++内的函数
5.1方法的定义和调用
//定义格式
public static 返回值数据类型 方法名 (参数1,参数2 ----) {
方法体
return 数据 ;
}
//调用格式
//有返回值
返回值数据类型 变量名 = 方法名 (参数1,残数2--- ) ;
//没有返回值
方法名 (参数1,残数2--- );
5.2方法的重载
在同一个类中定义多个具有相同名称的方法,但 参数列表 必须不同
前提
- 具有相同的方法名
- 参数不同,数量不同||类型不同
注意
1.与方法的调用无关,调用方式不变 2.与返回值无关
例子:
public class overload {
public static void main(String[] args) {
text1(); //我是空参数的方法
text1(10); // 我是一个参数的方法
text1(20,20); // 我的参数列表为 int n,int m
text1(20.1,20); // 我的参数列表为 double n,int m
}
public static void text1(){
System.out.println("我是空参数的方法");
}
public static void text1(int n){
//System.out.println(n);
System.out.println("我是一个参数的方法");
}
public static void text1(int n,int m){
//System.out.println(n);
//System.out.println(m);
System.out.println("我的参数列表为 int n,int m ");
}
public static void text1(double n,int m){
//System.out.println(n);
//System.out.println(m);
System.out.println("我的参数列表为 double n,int m ");
}
}
others
1.键盘录入(cin)
- c++ 内的cin
1 导包
import java.util.Scanner;
2 创建对象
Scanner sc = new Scanner(System.in); //sc为变量名
3 接收数据
接收数据的类型 变量名 = sc.nextInt()
2.基本数据类型和引用数据类型的区别
- 基本数据类型: 整数类型 浮点数类型 字符类型 布尔类型
- 引用数据类型: 所有的非基本数据类型都是引用数据类型,比如数组类型
区别:
1.存储位置不同 :
- 基本数据类型的内容存储在栈中
- 引用数据类型的内容存储在堆区,在栈中存储的是其内容所在的地址值
2.传递方式不同 :
- 基本数据类型变量,调用方法时作为参数是按数值传递的,无论被调用方法内此参数怎么变化,原本的那个数据不会被修改。
- 引用数据类型变量,调用方法时作为参数是按引用传递的,调用方法时若修改参数内容,原来的方法内的参数内容也会改变。因为他们指向的是同一块地址,内容是同一个。
例子 :
public class text1 {
public static void main(String[] args) {
//基本数据类型
int i = 1;
System.out.println(i);// 1 存储的是真实的值
text1(i); // 2
System.out.println(i); // 1 值没有被修改
//引用数据类型
int[] arr = {1,2,3,4,5,6,7,8,9};
System.out.println(arr); // [I@10f87f48 存储的是地址值
text2(arr); // 2
System.out.println(arr[0]); // 2 下标0处的值被修改了
}
// 基本数据类型
public static void text1(int n){
n = 2;
System.out.println(n);
}
//引用数据类型
public static void text2(int[] n){
n[0] = 2;
System.out.println(n[0]);
}
}
二 面向对象学习
基础概念
- 类是对现实生活中一类具有共同属性和行为的事物的抽象
- 属性:在类中通过成员变量来体现
- 行为:在类中通过成员方法来体现
0 类的定义和构造
public class 类名 {
// 构造方法
无参构造;
有参构造;
…
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2;
…
// 成员方法
方法1;
方法2;
}
创建类对象:
类名 类对象名 = new 类名(构造方法的参数);
类对象的使用:
对象名.成员方法()或者成员变量 ;
- 构造方法格式 : 类名(参数){ }
- 构造方法的作用是初始化类对象
- 构造方法可以重载
1.封装
对于封装的理解
将一个对象的属性和行为抽象出来放入一个类内
1.1 this指针
- 本质就是方法调用者的地址值
- 用于区分重名的成员变量和局部变量
例子:
class A{
int number; // 一个成员变量 number
void PrintNumber(){
int number = 10; //一个重名的局部变量
System.out.println(number); // 打印局部变量number 10
System.out.println(this.number); // 打印类内的number
}
}
1.2成员变量和局部变量
- 局部变量是方法内的变量,没有初始化值,使用前需要赋值,存储在栈上,生命周期与方法相同,作用域为当前方法
- 成员变量是类内方法外的变量,有初始化值,存储在堆中,生命周期与类相同,作用域是整个类内
2.继承
对于继承的理解
- 当类与类之间存在相同的属性和行为,就可以使用继承,使子类可以使用父类的属性和行为,优化代码
2.1 继承的实现
class 父类 {
...
}
class 子类 extends 父类 {
...
}
注意:
- 只能单继承,不能多继承,但可以多层继承
- 也就是说一个子类只能有一个父类,但是他的父类也可以有父类,相当于子类可以有爷爷,但是不能有多个父亲
2.2 继承的内容
- 成员变量都能继承,但是private的成员变量不能直接访问,需要super关键字
- 非private成员方法都能继承
- 构造方法不能继承
2.3 继承后的特点
- 没有重名的成员变量和方法直接访问
- 重名的成员变量和方法优先调用自己的(子类的)
- 使用super关键字可以调用父类的成员变量和方法 格式为: super.父类成员方法()或变量
2.4 重写
- 子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。
注意事项
- 权限大于等于父类权限。
- 返回值类型、函数名和参数列表都要一模一样。
- @Override重写注释 写在重写方法的上面
2.5 子类构造方法
- 子类构造方法内,默认调用父类的无参构造,即super()
- 可以调用父类带参构造方法 super(参数1,参数2)
- 父类构造方法必须写在子类构造方法的第一行
2.6 super
- super代表父类的地址值,与this并列,this表示本类的地址值
- super.父类成员变量 -- 调用父类成员变量
- super.父类成员方法() -- 调用父类成员方法
- super() -- 调用父类构造方法 ,括号内可填参数,根据传参不同代表调用父类不同构造方法
一个继承的例子 :
// 父类
class animal{
public int age;
public String name;
public void text1(){
System.out.println("I am animal");
}
public animal() {
System.out.println("animal的无参构造函数");
}
public animal(int age, String name) {
this.age = age;
this.name = name;
System.out.println("animal的有参构造函数");
}
}
//子类
class person extends animal{
@Override
public void text1(){
System.out.println("I am person");
System.out.println(super.getName());
}
public person() {
//super(13,"aaa");
this(22,"李四");
}
public person(int age, String name){
super(13,"aaa");
this.age = age;
this.name = name;
}
}
3 多态
对于多态的理解
- 同类型的对象,表现出不同形态
- 使用场景 : 传参时,需要传入不同对象,使用多态可以使用父类型作为参数,这个参数可以接收所有子类对象
3.1 多态的定义和前提
- 定义 父类类型 对象名称 = 子类对象
- 前提 1 有继承关系 2 有父类引用指向子类对象 3 有方法的重写
3.2 多态运行特点
- 调用成员变量时,编译看左边,运行看左边
- 调用成员方法时,编译看左边,运行看右边
- 解释 : 编译时,看父类有没有这个成员变量或者方法,没有就报错;运行时,调用成员变量时会调用父类变量,调用成员方法时会调用子类方法
3.2 多态的弊端以及解决方式
-
根据3.2 多态运行特点可知,即使子类有成员方法,但是父类没有,编译也不能通过,所以无法使用子类的特有方法
-
解决方式 : 强制类型转换 instanceof关键字
- 格式 : 变量名 instanceof 数据类型 。。。 如果变量属于该子类类型,返回true。 如果变量不属于该子类类型,返回false。
- 变量名 instanceof 数据类型 变量名。。。如果变量属于该子类类型,返回一个该子类类型的变量。如果变量不属于该子类类型,返回false。
例子 :
// 父类
class person1{
public int age;
public String name;
public person1(int age, String name){
this.age = age;
this.name = name;
}
public person1(){
}
public void show(){
System.out.println("我是父类person1");
}
}
// 子类1:
class student extends person1{
public int age;
public int id;
public student(){
}
public student(int age, String name){
super(age, name);
//System.out.println(super.age);
this.age = age-1;
this.name = "s" + name ;
}
@Override
public void show() {
System.out.println("我是子类student");
}
public void show2(){
System.out.println("我是子类student独有的方法 show2 ");
}
}
// 子类2:
class teacher extends person1{
@Override
public void show() {
System.out.println("我是子类teacher");
}
}
//测试类
public class a4polymorphism {
public static void main(String[] args) {
//
student s1 = new student();
teacher t1 = new teacher();
register(s1); //我是子类student
register(t1); //我是子类teacher
System.out.println(" ");
// 多态的运行特点
person1 p1 = new student(22,"Tom");
// 调用一个父类和子类都存在的成员变量 , 结果是调用父类的成员变量
System.out.println(p1.age); //22
// 调用一个父类和子类都存在的成员方法 , 结果是调用子类的成员方法
p1.show(); //我是子类student
// instanceof 关键字的使用
// 变量名 instanceof 数据类型 新变量名
// p1.show2(); 编译失败,因为show2是子类独有的方法,父类内不存在
if(p1 instanceof teacher p1t){
System.out.println("我是teacher");
} else if(p1 instanceof student p1s){
p1s.show2();
}else
{
System.out.println("没有这个类型,无法转换");
}
// 我是子类student独有的方法 show2
}
public static void register(person1 p1){
p1.show();
}
}
4 抽象类和接口
抽象类
- 抽象方法 : 没有方法体的方法称为抽象方法
- 抽象类 : 包含抽象方法的类
- 使用场景 :
- 强制让子类按照某种格式重写方法
- 抽取共性时,父类不需要写方法体,或无法确定方法体,就把方法定义为抽象的
- 注意
- 一个类内存在抽象方法,这个类必须被定义为抽象类
- 抽象类不能实例化,即不能创建对象
- 可以有构造方法
- 抽象方法必须被子类重写
例子 :
// 抽象类
abstract class A {
// 一个抽象方法
abstract public void show();
}
// 子类1
class A1 extends A {
public void show() {
System.out.println("我是子类A1的show方法");
}
}
// 子类2
class A2 extends A {
public void show() {
System.out.println("我是子类A2的show方法");
}
}
// 测试类
public class a5AbstractClass {
public static void main(String[] args) {
A a1 = new A1();
A a2 = new A2();
printlf(a1); // 我是子类A1的show方法
printlf(a2); // 我是子类A2的show方法
}
public static void printlf(A a){
a.show();
}
}
接口
package a7.others;
/*
接口 :
就是一种规则,是对行为的抽象,实现某种功能
定义 : public interface 接口名 {}
实现 :
成员变量 : 1 只能是常量 2 默认修饰符 public static final
成员方法一般定义为抽象方法,但也可以定义默认方法和静态方法和私有方法,
默认修饰符 public abstract
默认方法需要default修饰,不被强制重写
静态方法 static ,必须用接口名调用
私有方法 private
没有构造方法
接口之间可以继承,可以多继承
使用 : public class 类名 implements 接口1,接口2,接口3...{}
接口中的子类(实现类),要么重写接口中的所有抽象方法,要么是抽象类
实现类可以同时多个接口
实现类可以在继承的同时使用接口
*/
class bird implements Fly , Eat{
@Override
public void eat() {
System.out.println("bird EAT");
}
@Override
public void fly() {
System.out.println("bird Fly");
}
}
public class MY4interface {
public static void main(String[] args) {
bird b1 = new bird();
b1.eat();
b1.fly();
b1.fly2();
Fly.fly3();
}
}
fly接口
package a7.others;
public interface Fly {
// 构造方法
public abstract void fly();
//默认方法
public default void fly2() {
System.out.println("我是接口Fly的仿佛fly2");
}
//静态方法
static void fly3(){
System.out.println("我是接口Fly的仿佛fly3");
}
}
eat接口
package a7.others;
public interface Eat {
public abstract void eat();
}
others
1 static
package a5.Class;
// static 静态的,用于修饰成员变量或者方法
// 被static 修饰的成员变量和方法是属于类的,存放在堆区中的静态区,被所有的类对象共享,在内存中只存在一份
// 没有static修饰的成员变量和方法则是属于对象的,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问
// 静态变量,称为类变量或者静态成员变量。
// 定义格式 : 修饰符 static 数据类型 变量名 = 初始值;
// 直接用 类名访问即可。 类名.静态变量
// 静态方法
// 多用于测试类和工具类
// 直接用 类名访问即可。 类名.静态方法()
// javabean类 用来描述一类事物的类
// 测试类 用来检查其他类是否书写正确,带有main方法的类,是程序的入口
// 工具类 帮我们做一些事情的类,不用来描述一类事物
//注意事项,静态方法只能访问静态的东西,静态方法中没有this关键字
public class a1Static {
public static void main(String[] args) {
// 静态成员变量
phone1.screen = "玻璃" ;
System.out.println(phone1.screen); // 玻璃
// 静态成员方法
phone1.printScreen(); // 玻璃
}
}
class phone1{
static String screen;
static void printScreen(){
System.out.println(screen);
}
}
2 final
package a7.others;
/*
final 最终的
- 类:被修饰的类,叫做最终类,不能被继承。
- 方法:被修饰的方法,叫做最终方法,不能被重写。
- 变量:被修饰的变量,叫做常量,有且仅能被赋值一次。
// 常量
若修饰的是基本数据类型,则记录的值不能发生改
若修的的是引用数据类型,则记录的地址值不能发生改变,但指向值可以改变
命名规范: 字母大写,多个单词之间用下划线来分隔
*/
final class Fu{ }
//class Zi1 extends Fu{ } 报错,不能继承final的类
class Fu2{
final void show(){
System.out.println("我是父类 fu2 的show方法");
}
}
class zi2 extends Fu2{
// void show(){} 报错
}
//测试类
public class MY2final {
public static void main(String[] args) {
//
final int AGE = 3;
// AGE = 4 / 报错
//
final String s1 = "abcd";
// s1 = "zxcv"; 报错
//
final int[] a = {1,2,3};
int[] b = {3,4,5};
System.out.println(a);
System.out.println(b);
// a = b; a 被final修饰,存放的地址值不能变
b = a; // b 没被final修饰,可以修改指向的内存,而指向a指向的内存
System.out.println(b);
}
}
3 代码块
package a7.others;
/*
局部代码块 :
位置 : 写在方法内
作用 : 提前结束变量的生命周期 , 节约内存
执行时机 : 方法内从上到下一行行执行,执行到此代码块时执行
构造代码码 :
位置 : 类内成员位置
作用 : 抽取构造方法中的重复代码(不够灵活)
执行时机 : 创建本类对象的时候会先执行构造代码块再执行构造方法
静态代码块 : static{}
位置 : 类内成员位置
作用 : 数据的初始化,一般是静态成员变量
执行时机 : 随着类的加载而加载,并且自动触发,只执行一次
*/
class test01{
private String name;
public int age;
// 构造代码块1
{
System.out.println("我是构造代码块1");
}
public test01() {
System.out.println("我是无参构造方法");
}
public test01(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是有参构造方法");
}
// 构造代码块2
{
System.out.println("我是构造代码块2");
}
}
class test02{
private String name;
public int age;
static int A;
static{
System.out.println("我是静态代码块");
test02.A = 123;
}
public test02() {
System.out.println("我是无参构造方法");
}
public test02(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是有参构造方法");
}
}
public class MY3code_block {
public static void main(String[] args) {
// 局部代码块
{
int a = 3;
System.out.println(a); // 3
} // 运行到这,变量a就从内存中消失了
// System.out.println(a); 运行错误 a的作用域只在局部代码块内,即大括号内,外部并不能访问
// 构造代码块
test01 t1= new test01(); // 我是构造代码块1
// 我是构造代码块2
// 我是无参构造方法
// 静态代码块
test02 t2= new test02();// 我是静态代码块
// 我是无参构造方法
test02 t3= new test02("abcd",22); // 我是有参构造方法
}
}
4 内部类
package a7.others;
/*
内部类 : 在一个类内部再定义一个类
使用场景 : 一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
内部类可以调用外部类的所有属性和行为,外部类则必须先创建一个内部类的对象
//成员内部类
位置 : 类中成员位置
可被修饰词public等修饰
访问内部类的方式 : 外部类.内部类.
获取内部类对象的两种方式
1 在外部类中定义一个方法提供内部类的对象
2 外部直接创建成员内部类的对象(不是private修饰) : 外部类.内部类 变量 = new 外部类().new 内部类();
外部类成员与内部类成员重名 : Outer.this.变量名
Outer.this 代表的是外部类的地址值
//静态内部类
成员内部类的一种特殊形式,只能访问外部类的静态方法和变量
外部类.内部类 对象名 = new 外部类.内部类();
调用非静态方法的格式:对象名.方法
调用静态方法的格式:外部类名.内部类名.方法名()
//局部内部类
位置: 定义在方法内,类似局部变量
外界无法使用,只有在方法内部创建对象才可以使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
//匿名内部类 :
位置 : 可以写在成员位置,也可以写在局部位置
格式 :
new 类/接口(){
重写的方法
}
返回一个对象,一个类的子类对象,或者接口的实现类对象
使用场景 :
1 当方法的参数是接口或者类时,如果类对象只需要使用一次,就可以用匿名内部类简化代码
2 当我们想使用接口时
*/
// 外部类
class Outer {
private int a = 30;
// 在成员位置定义一个类
class Inner {
private int a = 20;
public void show() {
int a = 10;
System.out.println(a); // 10 答案:a
System.out.println(this.a); // 20 答案:this.a
System.out.println(Outer.this.a); // 30 答案:Outer.this.a
}
}
static class Inner2 {
int a = 20;
public void show2() {
System.out.println("我是静态成员内部类Inner2的方法show2");
//局部内部类
class DoubleInner{
void show() {
System.out.println(a);
}
}
//创建一个局部内部类
DoubleInner D1 = new DoubleInner();
D1.show();
}
static void show3() {
System.out.println("我是静态成员内部类Inner2的静态方法show3");
}
}
public void print(){
System.out.println("我是Outer类的print方法");
}
}
public class MY5innerClass {
public static void main(String[] args) {
// 成员内部类定义
Outer.Inner i1 = new Outer().new Inner();
// 分开写
// Outer o1 = new Outer();
// Outer.Inner i1 = o1.new Inner();
// 调用内部类的方法
i1.show(); //10 20 30
// 静态内部类的定义
Outer.Inner2 i2 = new Outer.Inner2();
// 调用方法
i2.show2(); //我是静态成员内部类Inner2的方法show2
// 调用静态方法
Outer.Inner2.show3(); // 我是静态成员内部类Inner2的静态方法show3
// 匿名内部类
Fly f1 = new Fly() {
@Override
public void fly() {
System.out.println("我是匿名内部类的fly");
}
};
f1.fly(); // 我是匿名内部类的fly
f1.fly2(); // 我是接口Fly的仿佛fly2
Outer oo1 = new Outer(){
@Override
public void print() {
System.out.println("我是匿名内部类的print方法");
}
};
oo1.print(); // 我是匿名内部类的print方法
}
}
三 .常用API
1 String
- String是一个封装好的类,代表字符串,与其他类相同,栈中只存放地址,内容存放在堆区
1.1 创建
两种方式
- 第一种 String 变量名 = new String()
- new创建的字符串对象,每次都会申请一个内存空间,即使内容相同,地址值也是不同的
- 第二种 String s = “XXXXXXX”
- 只要内容相同,无论创建几次,指向的都是同一块地址,在字符串池中维护
// public String(): 创建一个空白字符串对象,不含有任何内容
String s1 = new String();
System.out.println("s1:" + s1); // s1:
//String 变量名 = new String(char[] chs):根据字符数组的内容,来创建字符串对象
char[] c1 = {'a', 'b', 'c'};
String s2 = new String(c1);
System.out.println("s2:" + s2); //s2:abc
//String 变量名 = new String(byte[] bys) 根据字节数组的内容,来创建字符串对象
byte[] b1 = {97,98,99};
String s3 = new String(b1);
System.out.println("s3:" + s3); // s3:abc
//String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc
String s4 = "abc";
System.out.println("s4:" + s4); // s4:abc
1.2 字符串的比较
- ==号,比较基本数据类型: 比较的是具体的值 。比较引用数据类型:比较的是对象地址值
String str1 = "abc" ;
String str2 = "abc" ;
String str3 = new String("abc") ;
System.out.println((str1 == str2)); // true
System.out.println((str1 == str3)); // false
- equals 比较两个字符串内容是否相同、区分大小写 。。。。equalsIgnoreCa 不区分大小写
String str4 = "Abc" ;
boolean bo = str1.equals(str4);
System.out.println(bo); // false
bo = str1.equalsIgnoreCase(str4);
System.out.println(bo); // true
1.3 StringBuilder
-
StringBuilder 操作字符串的容器 常用于字符串的拼接和反转
-
需要导包 import java.util.StringJoiner;
-
定义 StringBuilder 变量名 = new StringBuilder();
-
方法 append reverse length tostring
// 1创建对象
StringBuilder sb1 = new StringBuilder();
// 2添加元素
sb1.append("abc");
sb1.append(2.3);
sb1.append(true);
System.out.println(sb1); // abc2.3true
// 3反转
sb1.reverse();
System.out.println(sb1); // eurt3.2cba
// 4获取长度
int len = sb1.length();
System.out.println(len); // 10
// 链式编程
// tostring 将StringBuilder类型转为String类型
String str1 = sb1.toString();
System.out.println(str1); // eurt3.2cba
StringBuilder sb2 = new StringBuilder(str1);
sb2.reverse().append(123).append("avc");
System.out.println(sb2); // abc2.3true123avc
1.4 StringJoiner
- StringJoiner 一个操作字符串的容器,可以设置指定的开始结束的符号,以及间隔符
StringJoiner joiner1 = new StringJoiner("---","[","]");
joiner1.add("abc").add("def").add("ghi");
System.out.println(joiner1); // [abc---def---ghi]
2 ArrayList
- 一个容器,存储的数据容量可以发生改变,但是只能存储引用数据类型
- import java.util.ArrayList;
- 定义 : ArrayList<数据类型> 变量名 = new ArrayList<数据类型>()
- 方法 add remove get size 具体实现看代码例子
import java.util.ArrayList;
public class MyArrayList {
public static void main(String[] args) {
// 创建集合
ArrayList<String> array1 = new ArrayList<String>();
// 添加元素
array1.add("a");
array1.add("b");
array1.add("c");
// ArrayList是java内置的一个类,类内对其打印对象做了处理,使其打印的不是地址值,而是其存储的所有数据内容
// 格式为[v1,v2,v3]
System.out.println(array1); // [a, b, c]
// 删除指定的元素,语法 : 集合名.remove(元素)
// 返回一个boolen类型的值
boolean b = array1.remove("a");
System.out.println(b); // true
System.out.println(array1); // [b, c]
//删除指定索引处的元素,返回被删除的元素
//语法 : 集合名.remove(下标值)
String s = array1.remove(0);
System.out.println(s); // b
System.out.println(array1); // [c]
//修改指定索引处的元素,返回被修改的元素
//语法: 集合名.remove(下标值,修改后的值)
s = array1.set(0,"d");
System.out.println(s); // c
System.out.println(array1); // [d]
//获取指定索引处的元素
// 集合名.get(下标值)
s = array1.get(0);
System.out.println(s); // d
// 返回集合中的元素的个数
// 集合名.size()
System.out.println(array1.size()); // 1
}
}
四 .异常
1. 异常
1.1 关于异常的一些概念
- 异常的概念 : 导致程序运行失败的事件就是异常
- 分类 :
- Error : 无法被处理
- Exception : 可以通过代码的方式纠正,使程序继续运行,是必须要处理的,分为checked编译时异常和runtime运行时异常。前者必须进行抛出声明throws或者捕获处理try...catch,否则不允许编译运行,后者不做强制要求。
1.2 声明异常 throws
- 如果方法内通过throw抛出了编译时异常,而没有捕获处理try...catch,那么必须通过throws进行声明,让调用者去处理。否则编译不通过。
- 格式 :修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
- 作用 :如果方法内出现声名的异常,则会在控制台打印异常的类型,原因,位置等信息,让调用者处理。(只抛出异常,不会对异常做出处理)
一个例子 :
package a8.Exception;
import java.io.FileOutputStream;
import java.io.IOException;
public class b3text {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\file\a.txt");
fos.write(97); // 文件内写入了一个字符 a
fos.close();
}
}
1.3 捕获异常 try…catch...finally
语法格式:
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
// 一般用于资源回收 , 比如IO流对象的关闭 , xxx.close()
}
细节 :
- 如果try语句中的代码没有异常,则跳过catch运行下一行代码
- 如果try语句内执行到了异常代码,则try内此异常代码后面的代码都不会执行,而是直接跳转到catch
- 如果不确定是哪一种异常,需要写多个catch与之对应,且父类一定要写在下面(多态,父类的形参可以接收所有子类的参数)
- 可以用 | (或) 在一个catch内捕获多个情况的异常
- 如果try内的的异常没有被捕获,则直接由JVM处理
- 无论有异常执行力catch语句还是没有异常,最后都要执行finally里的语句
一个例子 :
import java.io.FileOutputStream;
import java.io.IOException;
public class b3text {
public static void main(String[] args)throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("D:\file\a.txt\a.txt"); // 路径错误
fos.write(97);
} catch (IOException e) {
e.printStackTrace(); // 打印异常信息,包括类型,原因,位置等
}
finally {
if (fos != null) {
fos.close(); // 释放资源
}
}
}
}
// 控制台结果
java.io.FileNotFoundException: D:\file\a.txt\a.txt (系统找不到指定的路径。)
at java.base/java.io.FileOutputStream.open0(Native Method)
at java.base/java.io.FileOutputStream.open(FileOutputStream.java:289)
at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:230)
at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:118)
at a8.Exception.b3text.main(b3text.java:12)
1.4 抛出异常 throw
- throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行
- 格式 : throw new 异常类名(参数);
1.5 自定义异常
格式 :
public class MyException extends Exception{
public MyException(){
}
public MyException(String message){
super(message);
}
}
一个例子 :
import java.io.FileOutputStream;
import java.io.IOException;
public class b3text {
public static void main(String[] args) throws MyException {
getMAX(12,12);
}
public static int getMAX(int a,int b) throws MyException {
// 异常处理
if(a == b){
throw new MyException("他俩相等");
}
// 方法体
if(a>b){
return a;
}
return b;
}
}
//控制台信息 :
Exception in thread "main" a8.Exception.MyException: 他俩相等
at a8.Exception.b3text.getMAX(b3text.java:31)
at a8.Exception.b3text.main(b3text.java:24)
五. IO流
1 File类
1.1 File类的概念
- File 类是文件和目录路径名的抽象表示,一个File类对象可以表示一个真实的文件或者文件夹的路径
1.2 File类的构造方法
- 父路径 : 除掉文件或者文件夹自身的名字的路径。(绝对路径 - 此文件的名字)
- 子路径 : 文件或者文件夹自身的名字
File构造方法
- public File(String pathname) ` :通过将给定的路径名字符串来创建新的 File实例。
- public File(String parent, String child) ` :父路径名字符串和子路径名字符串创建新的 File实例。
- public File(File parent, String child)` :从父抽象路径名和子路径名字符串创建新的 File实例。
1.3 File类的常用方法
- public String getAbsolutePath() ` :返回此File的绝对路径 字符串。
- public String getPath() ` :将此File转换为路径名字符串。
- public String getName()` :返回由此File表示的文件或目录的名称。
- public long length()` :返回由此File表示的文件的长度。
- public boolean exists()` :此File表示的文件或目录是否实际存在。
- public boolean isDirectory()` :此File表示的是否为目录。
- public boolean isFile()` :此File表示的是否为文件。
- public boolean createNewFile()` :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
- public boolean delete()` :删除由此File表示的文件或空目录。不通过回收站。有内容的文件夹返回false删不掉。
- public boolean mkdir()` :创建由此File表示的目录。只能单级创建。一般不用,s那个更全能
- public boolean mkdirs()` :创建由此File表示的目录,可以多级创建
2 关于IO的一些概念
- IO,即input和output输入输出。在java内,输入输出是相对于java程序来说的
- 输入,把数据从硬盘上读取到java程序中的流。
- 输出,把数据从java程序中写出到硬盘上的流。
- 根据数据的类型分为:字节流和字符流。字节流,以字节为单位,读写数据。 字符流以字符为单位。
// 关于IO的一些基本概念
1 IO就是文件操作,I即in,进来,O即out,出去
2 输入也叫读取数据,从别的文件上读取数据到内存上(我们程序上)。
3 输出也叫写出数据,把数据从我们的程序上写出到其他的文件
// 字符集详解
0 ASCll GBK Unicode等都是编码(信息交换标准代码),将字母或者汉字等等人类的语言文字转换为唯一与之对应的一个二进制数
1 计算机中任何数据都是以二进制的形式来存储的
2 计算机内最小的存储单元是一个字节
3 ASCll字符集内一个英文占一个字节,且第一位为0
4 GBK字符集内一个汉字占两个字节,且高位字节的首位为1,为了区分字母,因为ASCll码表内的字节首位为0
5 Unicode字符集,万国码,UTF-8编码格式(Unicode transfer format)
6 Unicode字符集的UTF-8编码格式 一个英文占一个字节,二进制第一位为0 一个汉字占三个字节,二进制第一位为1
// 字符流原理解析
1 创建字符输入流对象
底层 : 关联文件 , 并创建缓冲区(长度为8196的字节数组)
2 读取数据
底层 : 1 判断缓冲区中是否有数据可以读取
2 缓冲区没有数据 : 就从文件中获取数据,放到缓冲区中,每次尽可能装满缓冲区
如果文件中也没有数据了,返回-1
3 缓冲区有数据 : 就从缓冲区中读取
空参的read方法: 一次读取一个字节,遇到中文就一次读多个字节,把字节解码并转成十进制返回
有参的read方法: 把读取字节,解码,强转三步合并了,返回的是字符
3 写出数据
底层 : 1 同读取数据read一样,也是先写到内存中的缓冲区上,缓冲区满了再将缓冲区上的内容写出到硬盘上指定的那个文件
2 可以使用flush刷新缓冲区,将缓冲区上的内容写出到文件 与close的区别是,flush只刷新缓冲区,而close不止刷新缓冲区,还会释放流对象,关闭与文件之间的通道,相当于c++的释放指针delete
3 基本字节流和字符流
3.1 基本字节流 FileOutputStream & FileInputStream
3.1.1 基本字节流输出流 FileOutputStream
构造方法 : (如果文件不存在,则自动创建)
public FileOutputStream(File file , boolean append):以File对象 , 第二个参数表示是否以追加模式,默认为false(清空文件后打开)
public FileOutputStream(String name , boolean append): 以指定的名称
方法 :
1 write(int b) 每次可以写出一个字节数据,根据对应的码表值写出对应的字符,下面同理,默认为UTF-8编码格式。
2 write(byte[] b) 每次可以写出数组中的数据
3 write(byte[] b, int off, int len) 写出指定长度字节数组 , off表示开始位置,len表示长度
一个用到的方法 :
public byte[] getBytes(String charsetName)
作用 :将字符串的每一个字母转换为对应的编码值,返回一个Byte类型的数组。默认为UTF-8.
一个例子 :
public class b1FileOutputStream3 {
public static void main(String[] args) throws IOException {
// 1 创建对象
FileOutputStream fos = new FileOutputStream("D:\file\a.txt",true);
// 2 写入数据
String str = "kankelaoye";
byte[] bytes = str.getBytes(); // ctrl + alt + v 自动生成返回值类型的对象
fos.write(bytes);
// 写出一个换行符
String str2 = "\r\n";
fos.write(str2.getBytes());
// 3.释放资源
fos.close();
}
}
3.1.2 基本字节流输入流 FileInputStream
- 构造方法基本相同 (但如果文件不存在,则报错,与输出流不同)
1. read()
1 一次只读一个字母,返回值是此字母对应的ASCll数字
2 每一次read都会移动一次指针,使指针指向下一个字节
3 读到空,返回-1,如果值是-1,占用两个位置,- 和 1。空格也是一个字节,对应的编码值为32
2. read(byte[] b) 每次读取b的长度个字节到数组中,返回值为读取到的有效字节个数,读取到末尾时,返回-1
例子 :
import java.io.FileInputStream;
import java.io.IOException;
public class b2FileInputStream3 {
public static void main(String[] args) throws IOException {
// 1 创建对象
FileInputStream fis = new FileInputStream("D:\file\a.txt");
// 2 读取数据
// 一次读取多个字节数组,具体读多少,跟数组的长度有关
// 返回值 : 本次读取到了多少个字节
byte[] b = new byte[1024];
int len = fis.read(b);
System.out.println(new String(b,0,len));
System.out.println(len);
// 3 释放资源
fis.close();
}
}
3.2 基本字符流 FileReader & FileWriter
3.2.1 基本字符输入流 FileReader
1. read()
返回读取到的字符的编码值,int类型。与字节流的read不同,如果遇到中文汉字,会读取多个字节,而字节流只读一个字节。
2. read(char[] cbuf)
每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1
public class b5FileReader2 {
public static void main(String[] args) throws IOException {
// 1
FileReader fr = new FileReader("D:\file\a2.txt");
// 2
// 读取数据 解码 强转三步合并,把强转之后的字符放到数组当中
// 相当于read + 强制类型转换
char[] buf = new char[2];
int len;
while ((len = fr.read(buf)) != -1) {
System.out.print(new String(buf, 0, len));
}
// 3
fr.close();
}
}
3.2.2 基本字符输出流 FileWriter
字符流相比于字节流的有点 : 可以直接写出字符串
void write(int c) 写入单个字符。 写出的是ASCll码表上对应的字符,不是数字本身。
void write(char[] buf) 写入字符数组。同上,也是对应的字符而不是数字本身
void write(char[] buf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str) 写入字符串。
void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
4 缓冲流 buffer
4.1 字节缓冲流 BufferedInputStream & BufferedOutputStream
底层依旧是基本流,但做了包装,增加了缓冲区,减少了与硬盘的交互次数,提高了读取或者写出的效率(性能)
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(路径));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(路径));
方法和基本字节流一样
4.2 字符缓冲流 BufferedReader & BufferedWriter
构造方法和字节缓冲流差不多,方法包含所有的基本字符流的方法
特有方法:
public String readLine(): 读一行文字。
public void newLine()`: 写一行行分隔符,由系统属性定义符号。
5 转换流
是字符流和字节流的桥梁
作用 1 : 指定字符集的读写(UTF-8 或者 GBK)
2 : 字节流想用字符流中的方法
package a9.IO.IO3;
/*
使用指定的编码格式读取一个文件
// 第一种方式
InputStreamReader 类
构造方法 :
* `InputStreamReader(InputStream in)`: 创建一个使用默认字符集的字符流。
* `InputStreamReader(InputStream in, String charsetName)`: 创建一个指定字符集的字符流。
// 第二种方式
FileReader 名称 = new FileReader(位置, Charset.forName(String charsetName));
使用指定的编码格式读取一个文件
*/
import java.io.*;
import java.nio.charset.Charset;
public class InputStreamReader1 {
public static void main(String[] args) throws IOException {
// 第一种方式
// 1 创建对象并指定字符编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("F:\黑马\Java基础-资料\day29-IO(其他流)\资料\gbkfile.txt"),"GBK");
// 2 读取数据
int c;
while( (c = isr.read()) != -1){
System.out.print((char)c);
}
// 3 释放资源
isr.close();
System.out.println(" ");
System.out.println(" ");
// 第二种方式
// 1 创建流对象
FileReader fr = new FileReader("F:\黑马\Java基础-资料\day29-IO(其他流)\资料\gbkfile.txt", Charset.forName("GBK"));
// 2 读取数据
int b;
while((b = fr.read()) != -1){
System.out.print((char)b);
}
// 3
fr.close();
}
}
package a9.IO.IO3;
/*
利用转换流按照指定字符编码写出
// 第一种方式 :
OutputStreamWriter 类
构造方法
OutputStreamWriter(OutputStream in)`: 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName)`: 创建一个指定字符集的字符流。
// 第二种方式 :
FileWriter 名字 = new FileWriter( 位置 , Charset.forName(string charsetName));
*/
import java.io.*;
import java.nio.charset.Charset;
public class OutputStreamWriter1 {
public static void main(String[] args) throws IOException {
// 第一种方式 :
// 1 创建转换流的对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\file\a222.txt"),"UTF-8");
// 2 写出数据
osw.write("你好!");
// 3释放资源
osw.close();
// 第二种方法
// 1 创建转换流的对象
FileWriter fw = new FileWriter("D:\file\a222.txt", Charset.forName("UTF-8"));
fw.write("你好你好!");
fw.close();
}
}
6 序列化
package a9.IO.IO4;
/*
序列化流 :
作用 : 把一个对象到本地文件中
构造方法 :
public ObjectOutputStream(OutputStream out) : 把基本流变成高级流
成员方法 :
public final void writeObject(Object obj) : 把对象序列化(写出)到文件中去
细节 :
1 NotSerializableException异常 , 需要类对象实现一个接口 Serializable
2 Serializable接口里面没有抽象方法,属于标记型接口,一旦实现了这个接口,就表示当前的这个类可以被序列化
3 理解 Serializable : 一个物品的合格证
4 想让某个属性不被保存到文件中,可以使用transient关键字,具体方法看Student类
5 使用序列化流将对象写到文件后,此文件中的数据不能修改,一旦修改就无法再次读回来了
*/
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStream1 {
public static void main(String[] args)throws IOException {
// 1 创建对象
Student stu = new Student(18,"zhangsan");
stu.gender = "男";
// 2 创建序列化流的对象/对象操作输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\file\a.txt"));
// 3 写出数据
oos.writeObject(stu);
// 4 释放资源
oos.close();
}
}
package a9.IO.IO4;
/*
反序列化流
作用 : 把文件中的对象读到程序当中
构造方法 : public ObjectInputStream(InputStream in) : 把基本流变成高级流
成员方法 : public Object readObject() : 把序列化到本地文件中的对象,读取到程序中
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStream1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1 创建反序列化流的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\file\a.txt"));
// 2 读取数据
Object o = ois.readObject(); // Object类是所有所有类的父类,何一个类时候如果没有明确的继承一个父类的话,那么它就是Object的子类
System.out.println(o); // 所以这里用了多态,一个父类的对象接收了一个子类对象
// 3
ois.close();
}
}
package a9.IO.IO4;
import java.io.Serial;
import java.io.Serializable;
public class Student implements Serializable{
// 版本号 , 自定义,修改类后也不会改变
@Serial
private static final long serialVersionUID = -4664212881104198966L;
int age;
String name;
// 被transient修饰,不会被序列化到本地文件内
transient String gender;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Student{age = " + age + ", name = " + name + "}";
}
}
7 打印流
package a9.IO.IO5;
/*
字节打印流 : PrintStream
构造方法 : public PrintStream(OutputStream , autoFlush , String encoding)
成员方法 :
1 常规方法 : write()
2 特有方法 : println() : 打印任意数据,自动刷新 , 自动换行
print() : 打印任何数据,不换行
printf() : 带有占位符的打印语句,不换行
*/
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public class PrintStream1 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
// 1
PrintStream ps = new PrintStream(new FileOutputStream("D:\file\a.txt"),true,"UTF-8");
// 2
ps.println(97);
ps.print(true);
ps.println();
ps.printf("%s 爱上了 %s","阿珍","阿强");
// 3
ps.close();
}
}
package a9.IO.IO5;
/*
PrintWriter : 字符打印流
构造方法和成员方法和字节打印流差不多
*/
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
public class PrintWriter1 {
public static void main(String[] args)throws IOException {
// 1
PrintWriter pw = new PrintWriter(new FileWriter("D:\file\a.txt"),true);
// 2
pw.println("你好!");
pw.print("你好啊啊啊");
pw.print("好好好");
pw.println();
pw.printf("%s爱上了%s","阿珍","阿强");
// 3
pw.close();
}
}
8 解压缩流
package a9.IO.IO6;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/*
// 解压缩流 : ZipInputStream
作用 : 解压一个压缩包
构造方法 : ZipInputStream 名字 = new ZipInputStream(new FileInputStream(压缩包所在的路径));
方法 : public ZipEntry getNextEntry() :读取下一个 ZIP 文件条目并将流定位到该条目数据的开始处(简述 : 按照既定的规则读取下一个文件或者文件夹,并将指针指向这个文件/文件夹,为了read读取这个文件)
read(byte[] b, int off, int len) : 从当前 ZIP 条目读入字节数组,和基本字节流的read一样,可以一次读取一个字节或者一个字节数组
// ZipEntry : 此类用于表示 ZIP 文件条目。
本质 : Zip压缩文件下文件或者文件夹的路径,和File类似
方法 : 也和File类似
isDictionary 判断此目录下是文件还是文件夹,文件夹返回true
getName 返回此目录下文件或者文件夹的名字 String类型
*/
public class ZipInputStream1 {
public static void main(String[] args) throws IOException {
// 1 创建一个File表示要解压的压缩包
File src = new File("D:\file\file1.zip");
// 2 创建一个File表示解压的目的地
File dest = new File("D:\file\file2");
// 调用方法
unzip(src, dest);
}
public static void unzip(File src, File dest) throws IOException {
// 解压的本质 : 把压缩包的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
// 创建一个解压缩流用来读取压缩包中的数据
ZipInputStream zis = new ZipInputStream(new FileInputStream(src));
// 获取到压缩包内的每一个ZipEntry对象
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
System.out.println(ze);
// 文件夹:需要在目的地dest处创建一个同样的文件夹
// 文件:需要读取到压缩包中的文件,并把它存放到目的地dest文件夹中(按照层级结构)
if (ze.isDirectory()) {
File file = new File(dest, ze.getName()); // File对象加String字符,拼接操作,返回一个完整的File对象
file.mkdirs();
// zis.closeEntry();
} else {
FileOutputStream fos = new FileOutputStream(new File(dest, ze.getName()));
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
// zis.closeEntry();
}
}
zis.close();
}
}
package a9.IO.IO6;
/*
压缩流 : ZipOutputStream
*/
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipOutputStream1 {
public static void main(String[] args) throws IOException {
// 1
File src = new File("D:\file\text2.png");
// 2
File dest = new File("D:\file\text2.zip");
// 3
toZip(src,dest);
}
public static void toZip(File src, File dest) throws IOException {
// 1
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
// 2
ZipEntry ze = new ZipEntry(src.getName());
// 3
zos.putNextEntry(ze);
// 4
FileInputStream in = new FileInputStream(src);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
zos.write(buf, 0, len);
}
in.close();
zos.closeEntry();
zos.close();
}
}