当前位置: 首页 > news >正文

政府网站群建设/浏览器下载安装2022最新版

政府网站群建设,浏览器下载安装2022最新版,网络营销型网站建设的内容,ps做网站的分辨率多少1、面向对象程序设计(OOP) 1.1、面向过程&面向对象 面向过程思想 步骤清晰简单,第一步做什么,第二步做什么…(线性思维)面向过程适合处理一些较为简单的问题 面向对象编程 物以类聚,分类的思维模式&#xff0c…

1、面向对象程序设计(OOP)


1.1、面向过程&面向对象

面向过程思想

  • 步骤清晰简单,第一步做什么,第二步做什么…(线性思维
  • 面向过程适合处理一些较为简单的问题

面向对象编程

  • 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索
  • 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

1.2、什么是面向对象?

  • 面向对象编程(Object-Oriented Programming, OOP)
  • 面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织(封装)数据。

抽象:编程思想!

面向对象三大特征

  • 封装
  • 继承
  • 多态

从认识论角度考虑是先有对象后有类。类,是抽象的,它是对象的抽象。而对象,是具体的事物。

从代码运行角度考虑是先有类后有对象。类是对象的模板

1.3、类与对象的关系

  • 类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物(类是一个模板)
    • 例:动物、植物、手机、电脑
    • Person类、Pet类、 Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
  • 对象是抽象概念的一个具体实例
    • 张三就是人类的一个具体实例,张三家里的旺财就是狗狗的一个具体实例
    • 能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念

类:

静态的属性 属性

动态的行为 方法

1.4、属性的定义

属性(property):字段(Field) 成员变量

默认初始化:

数字:0 0.0

char:u0000

boolean:false

引用:null

语法:修饰符 属性类型 属性名 = 属性值;

public String name = "张三";
public int name = 18;
public boolean adult = false;	//是否是成年人

1.5、创建与初始化对象

  • 使用new关键字创建对象

  • 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化,以及对类中构造器的调用

  • 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:

    1. 方法名和类的名字相同
    2. 没有返回类型,也不能写void

    构造方法的作用:主要用来初始化对象的值

    注:一旦定义了有参构造,系统不再提供默认无参构造方法,无参就必须显示定义

    //一个类即使什么也不行,它也会存在一个方法(无参构造)//显示定义构造器
    public Person(){}//实例化初始值	(有参构造)
    public Person(String name, int age) {this.name = name;this.age = age;
    }
    

    this关键字是对一个对象的默认引用(当前类的)

    6.5、创建对象内存

对象的引用:对象是通过引用来操作的:栈–>堆

2、面向对象三大特征之一封装


2.1、什么是封装?

封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问(隐藏内部细节,保留对外接口)

2.2、为什么需要封装?

我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。

记住这句话就够了:属性私有,get/set,方法公开

2.3、封装的使用

封装的步骤:

  1. 修改属性的可见性 设为private
  2. 创建公有的getter/setter方法 用于属性的读写
  3. 在getter/setter方法中加入属性控制语句 对属性值的合法性进行判断
//属性私有
private String name;	//姓名
private int age;	//年龄//提供一些可以操作这个属性的方法!
//提供一些public的get和set方法
public String getName() {	//获得这个数据return name;
}public void setName(String name) {	//给这个数据设置值this.name = name;
}public int getAge() {return age;
}public void setAge(int age) {if (age>100||age<0){this.age = 18;}else{this.age = age;}
}

封装的好处

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节,保留对外接口(get/set)
  3. 增强系统的可维护性

3、面向对象三大特征之一继承


3.1、什么是继承?

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模

  • 继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用extends来表示
  • 子类的父类之间,从意义上讲应该具有"is a"的关系

3.2、为什么要使用继承?

未使用继承前


将重复的代码抽取到父类中

使用继承优化后

3.3、继承的使用?

继承是代码重用的一种方式

将子类共有的属性和行为放到父类中

extends的意思是"扩展"。子类是父类的扩展

//在Java中,所有的类都直接或间接继承Object
//Person 人 父类(基类)
public class Person {protected String name;protected int age;protected String gender;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public void sayHi(){System.out.println("你好!");}
}//子类继承了父类,就会拥有父类的全部方法、属性
//学生 is 人    派生类(子类)
public class Student extends Person{private int StudentNo;public int getStudentNo() {return StudentNo;}public void setStudentNo(int studentNo) {StudentNo = studentNo;}
}public class Teacher extends Person {private String major;   //所教专业public String getMajor() {return major;}public void setMajor(String major) {this.major = major;}
}

注:子类不能继承父类的private成员、构造方法、子类与父类不在同包使用默认访问权限的成员

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

使用final修饰的类不能再被继承

3.4、super关键字

super关键字可以在子类访问父类方法,进而完成在子类的复用,super代表父类对象

注:

  • super调用父类的构造方法,必须在构造方法的第一个
  • super必须只能出现在子类的方法或者构造方法中!
  • super和this不能同时调用构造方法

super VS this

代表的对象不同:

  • this 表示当前对象引用,调用本类(包括继承)的属性、方法、本类构造方法
  • super:代表父类对象的引用,调用父类的属性、方法、本类构造方法

前提:

  • this:没有继承也可以使用
  • super:只能在继承条件才可以使用

构造方法:

  • this():本类的构造
  • super():父类的构造

注:this与super都要求在构造方法的首行,所以两者不能同时使用

3.5、方法的重写(Override)

方法重写的规则:

  • 前提条件:需要有继承关系,在不同类中,子类重写父类的方法!
  • 方法名必须相同
  • 参数列表必须相同
  • 返回值类型相同或者是其子类
  • 访问修饰符不能严于父类

重写:子类的方法和父类必须一致;方法体不同

//父类
//重写指的是方法的重写,跟属性无关
public class B {public void test(){System.out.println("B=>test");}
}//子类
public class A extends B{@Overridepublic void test() {System.out.println("A=>test");}
}public class Test {//静态方法和非静态的方法区别很大!//静态方法:方法的调用只和左边,定义的数据类型有关!//非静态:重写public static void main(String[] args) {A a = new A();a.test();	//A=>test//父类引用指向子类对象B b = new A();      //子类重写了父类的方法(向上转型)b.test();		//A=>test}
}

为什么需要重写?

父类的功能,子类不一定需要,或者不一定满足

访问修饰符


方法重载与方法重写

4、面向对象三大特征之一多态


4.1、什么是多态

生活中的多态:同一种操作,由于条件不同,产生的结果也不同

程序中的多态:同一个引用类型,使用不同的实例而执行不同操作(父类引用指向子类对象),从而产生多种形态

4.2、多态的使用

多态存在的条件:

  1. 有继承关系
  2. 子类重写父类方法
  3. 父类引用指向子类对象 Father f1 = new Son(); (自动类型转换)

注:多态是方法的多态,属性没有多态

父类和子类,有联系,类型装换异常!ClassCastException

不能被重写的方法:

  • static方法(属于类,不属于实例)
  • final 常量
  • private方法

4.3、instanceof关键字

多态的应用

场景一:使用父类作为方法形参实现多态,使方法参数的类型更为宽泛

package com.zhang.polymorphic;public class Animal {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public void eat() {System.out.println(this.name + "正在吃!");}
}
package com.zhang.polymorphic;public class Cat extends Animal{@Overridepublic void eat() {System.out.println("猫咪正在吃猫粮!");}public void run() {System.out.println("猫咪正在跑!");}
}
package com.zhang.polymorphic;public class Bird extends Animal {@Overridepublic void eat() {System.out.println("鸟正在捕食!");}public void run(){System.out.println("鸟正在飞翔!");}
}
package com.zhang.polymorphic;/*** 主人类*/
public class Master {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}/*** 喂猫咪** @param cat*///public void feed(Cat cat) {//    System.out.println(this.name + "喂食");//    cat.eat();//}/*** 喂鸟*///public void feed(Bird bird) {//    System.out.println(this.name + "喂食");//    bird.eat();//}//使用多态优化public void feed(Animal animal) {System.out.println(this.name + "正在喂食!");animal.eat();}/*** 购买动物*/public Animal buy(int type) {Animal animal = null;if (type == 1) {animal = new Cat();} else if (type == 2) {animal = new Bird();}return animal;}
}
package com.zhang.polymorphic;public class TestMaster {public static void main(String[] args) {Master master = new Master();master.setName("夏明");Cat cat = new Cat();Bird bird = new Bird();//喂食master.feed(cat);master.feed(bird);}
}

场景二:使用父类作为方法的返回值实现多态,使方法可以返回不同子类对象

package com.zhang.polymorphic;/*** 主人类*/
public class Master {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}/*** 喂猫咪** @param cat*///public void feed(Cat cat) {//    System.out.println(this.name + "喂食");//    cat.eat();//}/*** 喂鸟*///public void feed(Bird bird) {//    System.out.println(this.name + "喂食");//    bird.eat();//}//使用多态优化public void feed(Animal animal) {System.out.println(this.name + "正在喂食!");animal.eat();}/*** 购买动物*/public Animal buy(int type) {Animal animal = null;if (type == 1) {animal = new Cat();} else if (type == 2) {animal = new Bird();}return animal;}
}
package com.zhang.polymorphic;import java.util.Scanner;public class TestMaster2 {public static void main(String[] args) {Scanner input = new Scanner(System.in);System.out.println("欢迎来到xxx宠物市场");System.out.println("1.猫咪\t2.鸟");System.out.print("请输入:");int choice = input.nextInt();Master master = new Master();Animal animal = master.buy(choice);if (animal != null) {System.out.println("购买成功!");} else {System.out.println("购买失败!");}}
}

instanceof(强制类型转换)引用类型,判断一个对象是什么类型

package com.zhang.polymorphic;import java.util.Scanner;public class TestMaster2 {public static void main(String[] args) {Scanner input = new Scanner(System.in);System.out.println("欢迎来到xxx宠物市场");System.out.println("1.猫咪\t2.鸟");System.out.print("请输入:");int choice = input.nextInt();Master master = new Master();Animal animal = master.buy(choice);if (animal != null) {System.out.println("购买成功!");if (animal instanceof Cat) {((Cat) animal).run();} else if (animal instanceof Bird) {((Bird) animal).run();}} else {System.out.println("购买失败!");}}
}

多态

  • 子类转换为父类——向上转型,自动进行类型转换,父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法
  • 父类转换为子类——向下转型,通常结合instanceof运算符进行强制类型转换
  • 方便方法的调用,减少重复的代码!简洁

4.4、抽象类

//abstract  抽象类:类 extends:单继承~  (接口可以多继承)
public abstract class Action {//abstract 抽象方法,只有方法名字,没有具体的方法实现//约束~换而言之就是有人帮我们实现public abstract void doSomething();
}//抽象类的所有方法,只要其它子类继承了它,都必须要实现它的方法~ 除非子类也是抽象的
public class A extends Action{@Overridepublic void doSomething() {}
}

抽象类的特点:

  • 抽象类不能被实例化(只能靠子类去实现它;约束!)
  • 抽象类也可以编写普通方法

抽象方法的特点:

  • 抽象方法必须在抽象类中
  • 抽象方法没有方法体
  • 抽象方法必须在子类中被实现,除非子类是抽象类

抽象类存在的意义:提高开发效率,提高程序的可扩展性

5、接口


5.1、什么是接口?

定义接口使用interface关键字

//所有方法默认都是:public abstract
public interface MyInterface {public void foo()//其他方法
}

接口:只有规范!自己无法写方法~专业的约束!约束和实现分离:面向接口编程

接口的本质是契约,就像我们社会的法律一样,制定好大家都遵守

OOP的精髓,是对对象的抽象,最能体现这一点就是接口。为什么讨论设计模式,都只针对能力的语言(比如java、c++、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象(架构师)

微观概念:接口表示一种能力
接口的定义:代表了某种能力
方法的定义:能力的具体要求

接口与抽象类的异同

相同点:

  • 可编译成字节码文件
  • 不能创建对象
  • 可以作为引用类型
  • 具备Object类中所定义的方法

不同点:

  • 所有的属性都是公开静态常量,隐式使用public static final修饰
  • 所有的方法都是公开抽象方法,隐式使用public abstract修饰
  • 没有构造方法、动态代码块、静态代码块

5.2、面向接口编程

程序设计时:

  • 关心实现类有何能力,而不关心实现细节
  • 面向接口的约定而不考虑接口的具体实现

例:

  • 防盗门是一个门 (is a的关系)

  • 防盗门是有一个锁 (has a的关系)那么上锁、开锁就是它的能力

5.3、接口的使用

  1. 定义接口

    //interface 定义的关键字  接口都需要有实现类
    public interface UserService {//接口中的所有定义其实都是抽象的  public abstract//定义一些方法,让不同的人实现void add(String name);void del(String name);void update(String name);void check(String name);
    }public interface TimeService {void time();
    }
    
  2. 实现接口

    //类 可以实现接口!使用implement关键字实现接口
    //实现类接口的类,就需要重写接口中的方法
    //多继承~利用接口实现伪多继承!  可为类扩充多种能力
    public class UserServiceImpl implements UserService,TimeService{@Overridepublic void add(String name) {}@Overridepublic void del(String name) {}@Overridepublic void update(String name) {}@Overridepublic void check(String name) {}@Overridepublic void time() {}
    
  3. 使用接口

    public class Test {public static void main(String[] args) {UserService userService = new UserServiceImpl();userService.add("小张");}
    }
    

常见的关系

类与类:

  • 单继承
  • extends 父类名称

类与接口:

  • 多实现
  • implements 接口名称1,接口名称2,接口名称n

回调原理

5.4、接口的特性

  • 接口不可以被实例化,接口中没有构造方法
  • 接口中的方法都是抽象方法(public abstract)
  • 接口中的变量都是静态常量(public static final)
  • 实现类可以实现多个接口
  • 实现类必须实现接口的所有方法,除非此类为抽象类
  • 实现接口中的抽象方法是,访问修饰符必须是public

使用接口的好处:

  • 程序的耦合度降低
  • 更自然的使用多态
  • 设计与实现完全分离
  • 更容易搭建程序框架
  • 更容易更换具体实现

5.5、常量接口、标记接口

常量接口将多个常用于表示状态或固定值的变量,以静态常量的形式定义在接口中统一管理,提高代码可读性。

package com.zhang;/*** 常量接口*/
public interface ConstInterface {String Const1 = "value1";String Const2 = "value2";String Const3 = "value3";
}
package com.zhang;public class TestConstInterface {public static void main(String[] args) {if (ConstInterface.Const1 == "value1") {System.out.println("这是value1");}}
}

标记接口中没有包含任意成员,仅仅用作标记

  • Serializable(可序列化的)
  • Cloneable(可克隆的)

6、内部类


6.1、什么是内部类?

内部类就是在一个类的内部又定义了一个类,比如,A类中定义了一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了

成员内部类、静态内部类、局部内部类、匿名内部类

public class Outer {private int id = 100;public void out(){System.out.println("这是外部类的方法");}//成员内部类  添加static就是静态内部类public class Inner {public void in(){System.out.println("这是内部类的方法");}}//局部内部类public void method(){class  Inner{}}
}//一个java类中可以有多个class类,但只能有一个public class
class A{}public class Test {public static void main(String[] args) {//没有名字初始化类,不用将实例保存到变量中  匿名内部类new Apple().eat();new UserService(){@Overridepublic void hello() {}};}
}

注:不推荐使用

7、异常机制(Exception)


7.1、什么是异常?

生活中的异常:

正常情况下,小张每天开车去上班,耗时大约30分钟


但是,异常情况迟早要发生!

程序中的异常:

异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、接收非法参数等

异常处理的必要性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失

7.2、异常的分类

检查时异常(CheckedException):就是编译器要求你必须处理的异常。比如我们在编程某个文件的读/写时,编译器要求你必须要对某段代码try…catch… 或者 throws exception,这就是检查异常,简单的来说,你代码还没有运行,编码器就会检查你的代码,对可能出现的异常必须做出相对的处理。(比如当文件不存在时…)

运行时异常(RuntimeException):运行时异常是不需要捕获的,程序员可以不去处理,当异常出现时,虚拟机会处理。常见的运行时异常有空指针异常等

错误error:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的

7.3、异常体系结构

  • Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有错误和异常的超类

  • 在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception

    Error:

  • Error对象是由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关

  • Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError(内存溢出)这些异常发生是,Java虚拟机(JVM)一般会选择线程终止!(不能手动处理)

Exception

在Exception分支中有一个重要的子类RuntimeException(运行时异常)

Exception异常层次结构的父类
ArithmeticException算术异常情形,如以零作除数
ArrayIndexOutOfBoundsException数组下标越界
NullPointerException尝试访问 null 对象成员
ClassNotFoundException找不到类等异常,该类为不检查异常,程序中可以选择捕获,也可以不处理
IllegalArgumentException方法接收到非法参数
ClassCastException对象强制类型转换出错
NumberFormatException数字格式转换异常,如把"abc"转换成数字
MissingResourceException丢失资源
StackOverflowError栈溢出异常
ConcurrentModificationException并发修改异常

这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;

ErrorException的区别:

Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;

Exception通常情况下是可以被程序处理的,并且在程序中应尽可能的去处理这些异常

异常的传递

  • 异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息),并中断程序运行。

7.4、异常处理

Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws

捕获异常:

try------>执行可能产生异常的代码

catch------>捕获异常,并处理

finally------>无论是否发生异常,代码总能执行

public class Test {public static void main(String[] args) {int a = 10, b = 0;//假设要捕获多个异常:从小到大try {   //try 监控区域  可能出现异常的代码System.out.println(a / b);} catch (ArithmeticException ex) {    //catch(想要捕获的异常类型!) 捕获异常System.out.println("除数不能为0!");System.exit(1);} catch (Error ex) {System.out.println("Error");return;} catch (Exception ex) {System.out.println("Exception");} catch (Throwable ex) {System.out.println("Throwable");} finally {  //处理善后工作 最终都会执行,除非手动退出JVMSystem.out.println("程序结束!");}//finally,可以不要finally,假设I/O,资源,关闭,就需要使用finally了}
}

注:

  • 排列catch语句的顺序:先子类后父类
  • 发生异常时按顺序逐个匹配
  • 只执行第一个与异常类型匹配的catch语句
  • finally根据需求可写或不写

存在return的try-catch-finally块的执行顺序:

try(产生异常对象) -----> catch(异常类型匹配) -----> finally(执行finally块) -----> return (执行return,退出方法)

声明异常:

throws------>声明方法可能要抛出的各种异常

public static void sub(int a, int b) throws ArithmeticException {if (b == 0) {throw new ArithmeticException();    //throw 主动抛出异常,一般在方法中使用}System.out.println(a / b);
}//方式一:调用者,处理异常
public static void main(String[] args) {try {sub(6,0);} catch (Exception e) {e.printStackTrace();}
}//方式二:调用者,继续声明异常
public static void main(String[] args) throws Exception {sub(6,0);
}

如果在一个方法体中抛出了异常,如何通知调用者?

  1. 声明异常,多个异常用逗号隔开
  2. 方式一:调用者,处理异常;
  3. 方式二:调用者,继续声明异常;

使用原则:底层代码向上声明或者抛出异常,最上层一定要处理异常,否则程序中断

抛出异常:

throw------>手动抛出异常 语法:throw 异常对象

public class Test {public static void main(String[] args) throws Exception {Scanner input = new Scanner(System.in);System.out.print("请输入大于100的数字:");int num = input.nextInt();if (num < 100) {throw new Exception("输入的数字必须大于100");	//手动抛出异常} else {System.out.println("num值为:" + num);}}
}

7.5、自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可!

使用自定义异常类,大体分为以下几个步骤:

  1. 创建自定义异常类,继承Exception类
  2. 在方法中通过throw关键字抛出异常对象
  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;或者在方法的声明通过throws关键字指明要抛出给方法的调用者的异常,继续下一步操作
  4. 在出现异常方法的调用者中捕获并处理异常
package com.zhang;public class Person {private String gender;public Person(String gender) {this.gender = gender;}public Person() {}@Overridepublic String toString() {return "Person{" +"gender='" + gender + '\'' +'}';}public String getGender() {return gender;}public void setGender(String gender) throws Exception {if (gender.equals("男") || gender.equals("女")) this.gender = gender;else throw new GenderException("性别不符合要求!");}
}
package com.zhang;/*** 自定义异常类* (1)继承Exception或子类* (2)添加构造方法*/
public class GenderException extends Exception {public GenderException() {}public GenderException(String message) {System.out.println("GenderException:" + message);}
}
package com.zhang;public class TestPerson {public static void main(String[] args) {Person xiaozhang = new Person();try {xiaozhang.setGender("男1");} catch (Exception e) {e.printStackTrace();}System.out.println(xiaozhang);}
}//GenderException:性别不符合要求!

注:子类中的方法,不能抛出比父类更多、更宽的检查时异常

经验:

  • 处理运行时异常时,采用逻辑去合理的规避同时辅助try-catch处理
  • 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
  • 尽量添加finally语句块去释放占用的资源

8、集合框架


8.1、为什么使用集合框架?

例:存储一个班学员信息,假定一个班容纳20名学员,我们可以使用数组存储

如何存储每天的新闻信息?每天的新闻总数不确定,太少浪费空间,太多空间不足

如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象——可以使用Java集合框架

集合和数组的区别:

  • 数组声明了它容纳的元素的类型,而集合不声明
  • 数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。而集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。
  • 数组的存放的类型只能是一种(基本类型/引用类型),集合存放的类型可以不是一种(不加泛型时添加的类型是Object)。
  • 数组是Java语言中内置的数据类型,是线性排列的,执行效率或者类型检查都是最快的

8.2、Java集合框架

Collections:提供了对集合进行排序、遍历等多种算法实现

Collection 接口存储一组唯一(元素不可以重复),无序的对象

List 接口存储一组不唯一(元素可以重复),有序(无序是指存入元素的先后顺序与输出元素的先后顺序不一致)的对象

Set 接口存储一组唯一(元素不可重复),无序的对象

Map接口用于存储任意键值对,提供key到value的映射

  • 键:无序、无下标、唯一(元素不可以重复)
  • 值:无序、无下标、不唯一(元素可以重复)

8.3、List接口常见的实现类

List接口的实现类:ArrayList(长度可变的数组、存储空间连续) LinkedList链表存储(双向链表存储

ArrayList实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问(查询快)元素的效率比较高,增、删慢运行效率快、线程不安全(JDK1.2)

LinkedList采用双向链表存储方式。插入、删除(增删快)元素时效率比较高,查询慢;运行效率快,线程不安全

Vector 可实现自动增长的对象数组。 数组结构实现,遍历元素和随机访问(查询快)元素的效率比较高,增、删慢;运行效率慢、线程安全(JDK1.0)

List接口常用方法

方法名说 明
boolean add(Object o)在列表的末尾顺序添加元素,起始索引位置从0开始
void add(int index,Object o)在指定的索引位置添加元素。索引位置必须介于0和列表中元素个数之间
int size()返回列表中的元素个数
Object get(int index)返回指定索引位置处的元素。取出的元素是Object类型,使用前需要进行强制类型转换
boolean contains(Object o)判断列表中是否存在指定元素
boolean remove(Object o)从列表中删除元素
Object remove(int index)从列表中删除指定位置元素,起始索引位置从0开始
public class TestArrayList {public static void main(String[] args) {List<Student> students = new ArrayList<>();//初始化数据Student stu1 = new Student(1000, "张三", "男", "S1");Student stu2 = new Student(1001, "李四", "男", "S2");Student stu3 = new Student(1002, "王五", "女", "Y2");Student stu4 = new Student(1003, "赵六", "男", "S1");//添加到集合中students.add(stu1);students.add(stu2);students.add(stu3);students.add(stu4);//遍历输出System.out.println("共有" + students.size() + "位同学:");System.out.println("学号\t姓名\t性别\t年级");for (int i = 0; i < students.size(); i++) {Student sdt = students.get(i);	//逐个获取个元素 System.out.println(sdt.getStudentNo() + "\t" + sdt.getName() + "\t" + sdt.getGender() + "\t\t" + sdt.getGrade());}//删除下标为2的同学,从0开始students.remove(2);//添加到指定位置students.add(3, stu1);//判断是否包含指定学生if (students.contains(stu3)) {System.out.println("包含该学生");} else {System.out.println("不包含该学生!");}System.out.println("=====使用fori遍历=====");for (int i = 0; i < students.size(); i++) {Student sdt = students.get(i);System.out.println(sdt.getStudentNo() + "\t" + sdt.getName() + "\t" + sdt.getGender() + "\t\t" + sdt.getGrade());}System.out.println("=====使用迭代器遍历=====");Iterator it = students.iterator();while (it.hasNext()){Student stu = (Student) it.next();System.out.println(stu);}}
}

ArrayList源码分析

//源码分析
private static final int DEFAULT_CAPACITY = 10;		//默认容量
transient Object[] elementData; 		//存放元素的数组
private int size;			//实际元素个数public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);
}

注:如果没有集合中添加任何元素时,容量为0;添加任意一个元素时,容量为10,每次扩容大小是原来的1.5倍

LinkedList的特殊方法

方法名说 明
void addFirst(Object o)在列表的首部添加元素
void addLast(Object o)在列表的末尾添加元素
Object getFirst()返回列表中的第一个元素
Object getLast()返回列表中的最后一个元素
Object removeFirst()删除并返回列表中的第一个元素
Object removeLast()删除并返回列表中的最后一个元素
public class TestLinkedList {public static void main(String[] args) {LinkedList<Student> list = new LinkedList();//初始化数据Student stu1 = new Student(1000, "张三", "男", "S1");Student stu2 = new Student(1001, "李四", "男", "S2");Student stu3 = new Student(1002, "王五", "女", "Y2");Student stu4 = new Student(1003, "赵六", "男", "S1");list.addFirst(stu4);        //在第一个位置添加元素list.add(stu2);list.add(stu3);list.addLast(stu1);     //在最后一个位置添加元素System.out.println("集合中第一位同学的姓名是:"+list.getFirst().getName());  //获取集合中第一个元素System.out.println("集合中最后一位同学的姓名是:"+list.getLast().getName()); //获取集合中最后一个元素list.removeFirst();     //删除集合中第一个元素list.removeLast();      //删除集合中最后一个元素System.out.println("共有" + list.size() + "位同学:");//使用增强for循环遍历输出for (Student std : list) {System.out.println(std.getName() + "\t" + std.getName() + "\t" + std.getGender() + "\t" + std.getGrade());}}
}

8.4、Set接口常见的实现类

Set接口的实现类:HashSet(内部的数据结构是哈希表) TreeSet(基于红黑树(二叉查找树)实现的

HashSet是基于HashCode计算元素存放位置, 如果对象的hashCode值不同,则不用判断equals方法,就直接存到HashSet中。,当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。运行效率快、线程不安全

TreeSet是基于排列顺序实现元素不重复,实现了SortedSet接口,对集合元素进行排序,元素对象的类型必须实现Comparable接口,指定排列规则,通过CompareTo方法确认是否为重复元素,根据比较方法的返回结果是否为0,如果是0,则是相同元素,不存,如果不是0,则是不同元素,存储。运行效率快、线程不安全

HashSet的使用

package com.zhang.set;import java.util.Objects;/*** 人类*/
public class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}@Overridepublic int hashCode() {int n1 = this.name.hashCode();int n2 = this.age + 31; 	//(1) 31是一个质数(素数),只能被1和它自身整除的,减少散列冲突  (2)提供执行效率31*i=(i<<5)-ireturn n1 + n2;}@Overridepublic boolean equals(Object o) {//1.判断是不是同一个对象if (this == o) return true;//2.判断是否为空if (o == null) return false;//3.判断是否是Person类if (o instanceof Person) {Person person = (Person) o;//4.比较属性if (this.name.equals(person.getName()) && this.age == person.getAge()) {return true;}}//5.条件都不满足返回falsereturn false;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package com.zhang.set;import java.util.HashSet;
import java.util.Iterator;/*** Hashset集合的使用* 存储结构:哈希表(数组+链表+红黑树)* 存储过程:* (1)根据hashcode,计算保存的位置,如果此位置为空,则直接保存,如果不为空,执行第二步* (2)在执行equals方法,如果equals为true,则认为是重复元素,否则,形成链表*/
public class HashSetDemo03 {public static void main(String[] args) {//创建集合HashSet<Person> personHashSet = new HashSet<>();//1.添加数据Person p1 = new Person("小赵", 18);Person p2 = new Person("小张", 19);Person p3 = new Person("小林", 20);Person p4 = new Person("小李", 21);personHashSet.add(p1);personHashSet.add(p2);personHashSet.add(p3);personHashSet.add(p4);//重复  不能添加了//personHashSet.add(p4);personHashSet.add(new Person("小林", 20));System.out.println("元素个数:" + personHashSet.size() + "\n" + personHashSet);//2.删除元素personHashSet.remove(p2);//如果重写hashcode和equals是可以删除的  否则反之//personHashSet.remove(new Person("小李", 21));System.out.println("删除之后元素个数:" + personHashSet.size() + "\n" + personHashSet);//3.遍历元素//3.1使用增强for循环遍历System.out.println("=====使用增强for循环遍历=====");for (Person person: personHashSet) {System.out.println(person);}//3.2使用迭代器循环遍历System.out.println("=====使用迭代器循环遍历=====");Iterator<Person> it = personHashSet.iterator();while (it.hasNext()) {System.out.println(it.next());}//判断//重写hashcode和equals是寻找的是同一个对象  结果为true  否则为falseSystem.out.println(personHashSet.contains(new Person("小林",20)));System.out.println(personHashSet.isEmpty());}
}

注:HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。

TreeSet的使用

package com.zhang.set;import java.util.Objects;/*** 人类*/
public class Person implements Comparable<Person> {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}//先按照姓名比较 ,然后再按年龄比较@Overridepublic int compareTo(Person o) {int n1 = this.getName().compareTo(o.getName());int n2 = this.getAge() - (o.getAge());return n1 == 0 ? n2 : n1;}
}
package com.zhang.set;import java.util.Iterator;
import java.util.TreeSet;/*** treeSet集合的使用* 存储结构:红黑树* 无序 无下标 不能重复* 要求:元素必须要实现Comparable接口,compareTo()方法的返回值为0,证明是重复元素的如果不实现将会报错:Exception in thread "main" java.lang.ClassCastException: com.zhang.set.Person cannot be cast to java.lang.Comparable*/
public class TreeSetDemo02 {public static void main(String[] args) {//创建treeSet集合TreeSet<Person> personTreeSet = new TreeSet<>();//1.添加数据Person p1 = new Person("xyz", 18);Person p2 = new Person("abc", 19);Person p3 = new Person("efg", 20);Person p4 = new Person("ABCD", 21);Person p5 = new Person("ABCD", 22);personTreeSet.add(p1);personTreeSet.add(p2);personTreeSet.add(p3);personTreeSet.add(p4);personTreeSet.add(p5);//重复了 不能添加personTreeSet.add(p5);System.out.println("元素个数:" + personTreeSet.size() + "\n" + personTreeSet);//删除元素personTreeSet.remove(new Person("ABCD", 21));System.out.println("删除之后元素个数:" + personTreeSet.size() + "\n" + personTreeSet);//3.遍历元素//3.1使用增强for循环遍历System.out.println("=====使用增强for循环遍历=====");for (Person letter : personTreeSet) {System.out.println(letter);}//3.2使用迭代器循环遍历System.out.println("=====使用迭代器循环遍历=====");Iterator<Person> it = personTreeSet.iterator();while (it.hasNext()) {System.out.println(it.next());}//4.判断System.out.println(personTreeSet.contains(p4));System.out.println(personTreeSet.isEmpty());}
}

使用Comparator实现定制比较(比较器)

package com.zhang.set;import java.util.Comparator;
import java.util.TreeSet;/*** TreeSet集合的使用* Comparator:实现定制比较(比较器)* Comparable 可比较的*/
public class TreeSetDemo03 {public static void main(String[] args) {//创建treeSet集合,并指定比较规则TreeSet<Person> personTreeSet = new TreeSet<>(new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {int n1 = o1.getAge() - o2.getAge();int n2 = o1.getName().compareTo(o2.getName());return n1 == 0 ? n2 : n1;}});//1.添加数据Person p1 = new Person("xyz", 18);Person p2 = new Person("abc", 19);Person p3 = new Person("efg", 20);Person p4 = new Person("ABCD", 21);Person p5 = new Person("ABCD", 22);Person p6 = new Person("ailin", 20);personTreeSet.add(p1);personTreeSet.add(p2);personTreeSet.add(p3);personTreeSet.add(p4);personTreeSet.add(p5);personTreeSet.add(p6);System.out.println("元素个数:" + personTreeSet.size() + "\n" + personTreeSet);}
}

注:TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。

8.5、Map接口常见的实现类

Map接口专门处理键值映射数据的存储,可以根据键实现对值的操作

Map接口的实现类:HashMap(基于哈希表实现)和TreeMap(基于红黑树(二叉查找树)实现的

HashMap通过hashcode对其内容进行快速查找,允许用null作为key或是value,运行效率快、线程不安全(JDK1.2)

TreeMap中所有的元素都保持着某种固定的顺序,实现了SortedMap接口(是Map的子接口),可以对key自动排序,运行效率快、线程不安全(JDK1.2)

HashTable<K,V>也是一种key-value结构,它继承自Dictionary<K,V>,实现了Map<K,V>和Cloneable以及Serializable接口。(基本已被淘汰),不允许用null作为key或是value,运行效率慢、线程安全(JDK1.0)

Map接口常用方法

方法名说 明
Object put(Object key, Object val)以“键-值对”的方式进行存储
Object get (Object key)根据键返回相关联的值,如果不存在指定的键,返回null
Object remove (Object key)删除由指定的键映射的“键-值对”
int size()返回元素个数
Set keySet ()返回此映射中包含的键的Set集合
Collection values ()返回值的集合
boolean containsKey (Object key)如果存在由指定的键映射的“键-值对”,返回boolean
entrySet()返回此映射中包含的映射关系的Set集合

HashMap的使用

package com.zhang.map;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;/*** Map接口的使用* 特点:(1)存储键值对(2)键不能重复,值可以重复(3)无序,无下标*/
public class HashMapDemo01 {public static void main(String[] args) {//创建Map集合Map<String, Country> map = new HashMap<>();//1.添加元素Country country1 = new Country("CN", "中国");Country country2 = new Country("RU", "俄罗斯联邦");Country country3 = new Country("FR", "法兰西共和国");Country country4 = new Country("US", "美利坚联合众国");map.put(country1.getLog(), country1);map.put(country2.getLog(), country2);map.put(country3.getLog(), country3);map.put(country4.getLog(), country4);//由于键重复,无法添加    但是把前面的value给替换了map.put(country1.getLog(), new Country("CHINA", "中华人民共和国"));System.out.println("元素个数:" + map.size() + "\n" + map);//2.删除map.remove("US");System.out.println("删除之后元素个数:" + map.size() + "\n" + map);//3.遍历//3.1、使用keySet()方法遍历System.out.println("=====使用keySet()方法遍历=====");//Set<String> keySet = map.keySet();for (String countryKey : map.keySet()) {System.out.println(countryKey + "\t\t" + map.get(countryKey));}//3.2、使用entrySet()方法遍历  entrySet效率要高于keySetSystem.out.println("=====使用entrySet()方法遍历=====");//Set<Map.Entry<String, String>> entries = map.entrySet();for (Map.Entry<String, Country> countryEntry : map.entrySet()) {System.out.println(countryEntry.getKey() + "\t\t" + countryEntry.getValue());}//判断System.out.println(map.containsKey("CN"));      //是否包含该键System.out.println(map.containsValue(new Country("CHINA", "中华人民共和国")));      //是否包含该值System.out.println(map.isEmpty());}
}
package com.zhang.map;import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** HashMapMap接口的使用* 存储结构:哈希表(数组+链表+红黑树)* 使用key的hashCode和equals作为重复*/
public class HashMapDemo02 {public static void main(String[] args) {//创建HashMap集合HashMap<Student, String> hashMap = new HashMap<>();//添加元素Student s1 = new Student(1001, "张三");Student s2 = new Student(1002, "李四");Student s3 = new Student(1003, "王五");Student s4 = new Student(1004, "赵六");hashMap.put(s1, "北京");hashMap.put(s2, "上海");hashMap.put(s3, "西安");hashMap.put(s4, "广州");//key不能重复,无法添加hashMap.put(s1, "广东");hashMap.put(new Student(1004, "赵六"), "杭州");System.out.println("元素个数:" + hashMap.size() + "\n" + hashMap);//2.删除hashMap.remove(s4);System.out.println("删除之后元素个数:" + hashMap.size() + "\n" + hashMap);//3.遍历//3.1、使用keySet()方法遍历System.out.println("=====使用keySet()方法遍历=====");//Set<Student> students = hashMap.keySet();for (Student student : hashMap.keySet()) {System.out.println(student + "\t\t" + hashMap.get(student));}//3.2、使用entrySet()方法遍历  entrySet效率要高于keySetSystem.out.println("=====使用entrySet()方法遍历=====");//Set<Map.Entry<Student, String>> entries = hashMap.entrySet();for (Map.Entry<Student, String> entries : hashMap.entrySet()) {System.out.println(entries.getKey() + "\t\t" + entries.getValue());}//4.判断System.out.println(hashMap.containsKey(new Student(1003, "王五")));      //是否包含该键System.out.println(hashMap.containsValue("杭州"));      //是否包含该值System.out.println(hashMap.isEmpty());}
}

HashMap源码分析

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16  hashMap初始容量大小
static final int MAXIMUM_CAPACITY = 1 << 30;		//hashMap的最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;		//默认加载因子
static final int TREEIFY_THRESHOLD = 8;				//jdk1.8  当链表长度大于8时,调整为红黑树
static final int UNTREEIFY_THRESHOLD = 6;			//jdk1.8  当链表长度小于6时,调整为链表
static final int MIN_TREEIFY_CAPACITY = 64;			//jdk1.8  当链表长度大于8时,并且集合元素个数大于等于64时,调整为红黑树
transient Node<K,V>[] table;	//哈希表中的数组
transient int size;		//元素个数

总结:

  • HashMap刚创建时, table是null, 为了节省空间,当添加第一个元素时,table容量调整为16
  • 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数。
  • jdk1.8 当每个链表长度大于8,并且元素个数大于等于64时, 会调整为红黑树,目的提高执行效率
  • jdk1.8 当链表长度小于6时,调整成链表
  • jdk1.8以前链表时头插入,jdk1.8以后时是尾插入

TreeMap的使用

package com.zhang.map;import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;/***  TreeMap集合的使用*  特点:(1)存储键值对(2)键不能重复,值可以重复(3)无序,无下标*  注:treeMap也是需要定制比较的 否则报错:Exception in thread "main" java.lang.ClassCastException: com.zhang.map.Student cannot be cast to java.lang.Comparable*/
public class TreeMapDemo01 {public static void main(String[] args) {//创建treeMap集合  定制比较TreeMap<Student, String> treeMap = new TreeMap<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {int n1 = o1.getStudentNo() - o2.getStudentNo();return n1;}});//1.添加元素Student s1 = new Student(1001, "张三");Student s2 = new Student(1002, "李四");Student s3 = new Student(1003, "王五");Student s4 = new Student(1004, "赵六");treeMap.put(s1, "北京");treeMap.put(s2, "上海");treeMap.put(s3, "广东");treeMap.put(s4, "深圳");treeMap.put(new Student(1004, "赵六"), "西安");System.out.println("元素个数:" + treeMap.size() + "\n" + treeMap);//2.删除元素treeMap.remove(new Student(1003, "王五"));System.out.println("删除之后元素个数:" + treeMap.size() + "\n" + treeMap);//3.遍历//3.1、使用keySet()方法遍历System.out.println("=====使用keySet()方法遍历=====");for (Student student : treeMap.keySet()) {System.out.println(student + "\t\t" + treeMap.get(student));}//3.2、使用entrySet()方法遍历  entrySet效率要高于keySetSystem.out.println("=====使用entrySet()方法遍历=====");for (Map.Entry<Student, String> entries : treeMap.entrySet()) {System.out.println(entries.getKey() + "\t\t" + entries.getValue());}//4.判断System.out.println(treeMap.containsKey(new Student(1003, "王五")));      //是否包含该键System.out.println(treeMap.containsValue("北京"));      //是否包含该值System.out.println(treeMap.isEmpty());}
}

8.6、泛型

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递
  • 常见形式有泛型类、泛型接口、泛型方法

语法:

<T,...> 	//T称为类型占位符,表示一种引用类型

好处:

  • 提高代码的重用性
  • 防止类型转换异常,提高代码的安全性

泛型集合

概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致

特点:

  • 编译时即可检查,而非运行时抛出异常
  • 访问时,不必类型转换(拆箱)
  • 不同泛型直接引用不能相互赋值,泛型不存在多态

Collections工具类

package com.zhang.collection;import java.util.*;/*Collections工具类的使用*/
public class CollectionDemo03 {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(455);list.add(498);list.add(266);list.add(100);list.add(36);System.out.println("排序之前:" + list);//sort排序Collections.sort(list);System.out.println("排序之后:" + list);//binarySearch  二分查找器   找到集合中的元素下标  找不到返回-1int search = Collections.binarySearch(list,266);System.out.println(search);//copy  复制List<Integer> integerList = new ArrayList<>();for (int i = 0; i < list.size(); i++) {integerList.add(0);}Collections.copy(integerList, list);System.out.println("copy完成的集合数据:" + integerList);//reverse  反转Collections.reverse(integerList);System.out.println("反转之后:" + integerList);//shuffle  打乱  相当于洗牌Collections.shuffle(integerList);System.out.println("随机打乱之后:" + integerList);//集合转为数组System.out.println("=====集合转为数组=====");Integer[] array = list.toArray(new Integer[0]);System.out.println("数组长度:" + array.length + "\n" + Arrays.toString(array));//数组转为集合System.out.println("=====数组转为集合=====");String[] names = {"小赵", "小张", "小林", "小李", "小飞"};//数组转为集合是一个受限集合,不能添加和删除  否则报错:Exception in thread "main" java.lang.UnsupportedOperationExceptionList<String> namesList = Arrays.asList(names);//namesList.add("小刚");//namesList.remove(0);System.out.println(namesList);//把基本类型数组转为集合时,需要修改为包装类Integer[] nums = {10, 2066, 9966, 8875, 100};List<Integer> numsList = Arrays.asList(nums);System.out.println(numsList);}
}

8.7、迭代器Iterator实现遍历

  • 获取Iterator :Collection 接口的iterator()方法
  • Iterator的方法:
    • boolean hasNext(): 判断是否存在另一个可访问的元素
    • Object next(): 返回要访问的下一个元素
//使用迭代器遍历输出  iterator
Set<String> keys = countryMap.keySet();     //取出所有key的集合
Iterator<String> ctIterator = keys.iterator();     //获取Iterator对象
while (ctIterator.hasNext()) {String key = ctIterator.next();     //取出keyCountry country = countryMap.get(key);	System.out.println(country.getLog() + "\t\t" + country.getCtName());
}

使用foreach实现遍历(较为常用)

for (Country country : countryMap.values()) {System.out.println(country.getLog() + "\t" + country.getCtName());
}

8.8、Properties(属性集合)

继承自Hashtable,线程安全

  • 存储属性名和属性值
  • 属性名和属性值都是字符串类型
  • 没有泛型
  • 和流有关
package com.zhang.properties;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Properties;
import java.util.Set;/*** Properties属性集合的使用*/public class PropertiesDemo01 {public static void main(String[] args) throws Exception {//1.创建Properties集合Properties properties = new Properties();//2.添加属性properties.setProperty("userName", "MrZhang");properties.setProperty("password", "666666");System.out.println(properties);//3.删除属性//properties.remove("userName");//System.out.println("删除之后:" + properties);//4.遍历//4.1、使用keySet遍历System.out.println("=====使用keySet遍历=====");for (Object o : properties.keySet()) {System.out.println("key:" + o + "\t\tvalue:" + properties.get(o));}//4.2、使用entrySet遍历System.out.println("=====使用entrySet遍历=====");for (Map.Entry<Object,Object> entry : properties.entrySet()) {System.out.println("key:" + entry.getKey() + "\t\tvalue:" + entry.getValue());}//4.3、使用stringPropertyNames()方法遍历System.out.println("=====使用stringPropertyNames()方法遍历=====");Set<String> propertyNames = properties.stringPropertyNames();for (String property : propertyNames) {System.out.println("key:" + property + "\t\tvalue:" + properties.get(property));}//5.和流有关的方法//System.out.println("=====list()方法列表=====");//PrintWriter pw = new PrintWriter("properties.txt");//properties.list(pw);//pw.close();//System.out.println("打印成功!");System.out.println("=====store()方法保存=====");FileOutputStream fos = new FileOutputStream("store.properties");properties.store(fos, "key---value");fos.close();System.out.println("创建成功!");System.out.println("=====load()方法加载=====");Properties properties2 = new Properties();FileInputStream fis = new FileInputStream("store.properties");properties2.load(fis);fis.close();System.out.println(properties2);}
}

9、多线程


9.1、什么是多线程?

在了解多线程之前,需要先了解多任务、线程、进程这几个名词

所谓多任务就是在同一时间做多种事情,例如window系统的任务管理器

在操作系统中运行的程序就是进程,比如QQ、播放器、IDE、游戏等

一个进程可以有多个线程,如视频中同时听到声音、看见图像、弹幕等

如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程,多个线程交替占用CPU资源,而非真正的并行执行

9.2、进程(Process)与线程(Thread)

  • 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
  • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然就没有存在的意义了,线程是CPU调度和执行的单位

普通方法调用和多线程

  • 线程就是独立的执行路径
  • 在程序运行中,即使自己创建线程,后台也会有多个线程,如主线程(用户线程),gc线程(垃圾回收~守护线程
  • main()称为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

9.3、线程的创建方式

  1. 继承java.lang.Thread类

    1. 自定义线程类继承Thread类
    2. 重写run()方法,编写线程执行体
    3. 创建线程对象,调用start()方法启动线程
    //创建线程方式一:继承Thread类  重写run方法,调用start开启线程
    public class MyThread extends Thread {@Overridepublic void run() {//run方法线程体for (int i = 0; i < 20; i++) {System.out.println("我在看代码~" + i);}}public static void main(String[] args) {//main线程MyThread myThread = new MyThread();         //创建线程对象myThread.start();       //调用start()方法for (int i = 0; i < 200; i++) {System.out.println("我在学习多线程~" + i);}}
    }
    

    注:线程开启不一定立即执行,有CPU调度执行

  2. 实现java.lang.Runnable接口

    1. 定义MyRunnable类实现Runnable接口
    2. 实现run()方法,编写线程执行体
    3. 创建线程对象,调用start()方法启动线程
    //龟兔赛跑
    public class Race implements Runnable {private static String winner;   //获胜者@Overridepublic void run() {for (int i = 0; i <= 200; i++) {//模拟兔子休息if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}boolean flag = gameOver(i);if (flag) break;System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");}}//游戏结束public boolean gameOver(int steps) {//判断是否有胜利者if (winner != null) {   //已经有胜利者了return true;}{if (steps == 200) {  //跑完了winner = Thread.currentThread().getName();System.out.println("winner is" + winner);return true;}}return false;}public static void main(String[] args) {Race run = new Race();new Thread(run, "乌龟").start();new Thread(run, "兔子").start();}
    }
    

    比较两种创建线程的方式:

    继承Thread类

    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用,避免OOP单继承局限性

    实现Runnable接口

    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:灵活方便,方便同一个对象被多个线程使用

    注:多个线程操作统一共享资源时,将引发数据不安全问题

9.4、静态代理模式

//静态代理模式:真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:代理对象可以做很多真实对象做不了的事情
//真实对象专注做自己的事情
public class StaticProxy {public static void main(String[] args) {You you = new You();    //你要结婚//        WeddingCompany weddingCompany = new WeddingCompany(you);//        weddingCompany.happyMarry();new WeddingCompany(you).happyMarry();}
}interface Marry{void happyMarry();
}
//真实角色,你去结婚
class You implements Marry{@Overridepublic void happyMarry() {System.out.println("张先生要结婚了!真开心");}
}//代理角色,帮助你结婚
class WeddingCompany implements Marry{//代理谁--> 真实目标角色private Marry target;public WeddingCompany(Marry target) {this.target = target;   //这是真实对象}@Overridepublic void happyMarry() {before();this.target.happyMarry();after();}private void before() {System.out.println("结婚之前,布置现场");}private void after() {System.out.println("结婚之前,收尾款");}
}

9.5、线程状态

线程方法

说 明
setPriority(int newPriority)更改线程的优先级
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
void join()等待该线程终止
static void yield()暂停当前正在执行的线程对象,并执行其他线程
void interrupt()中断线程(尽量少用
boolean isAlive()测试线程是否处于活动状态

打印当前时间

public class TestCurrTime {public static void main(String[] args) {//打印当前系统时间Date currDate = new Date(System.currentTimeMillis()); //获取当前系统时间while (true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(currDate));currDate = new Date(System.currentTimeMillis()); //更新当前系统时间} catch (InterruptedException e) {e.printStackTrace();}}}
}

买票

//多个线程同时操作同一个对象
//多个线程操作同一个资源的情况下,线程不安全,数据紊乱  模拟网络延时,放大问题的发生性
public class TestTicket {public static void main(String[] args) {BuyTickets station = new BuyTickets();new Thread(station, "小明").start();new Thread(station, "黄牛党").start();new Thread(station, "夏林").start();}
}//买票
class BuyTickets implements Runnable {int ticketNums = 10;boolean flag = true;@Overridepublic void run() {buy();}private void buy() {while (flag) {try {Thread.sleep(100);System.out.println(Thread.currentThread().getName() + "抢到了" + (ticketNums--) + "张票");if (ticketNums <= 0) flag = false;     //如果在没有票的情况下} catch (InterruptedException e) {e.printStackTrace();}}}
}

9.6、守护(daemon)线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕(main)
  • 虚拟机不用等待守护线程执行完毕(gc)
  • 如:后台记录操作日志、监控内存、垃圾回收等

9.7、线程同步

为什么需要线程同步?

多个线程操作同一共享资源时,将引发数据不安全问题

并发:同一个对象多个线程同时操作

现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决方法就是:排队一个一个来

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程在使用

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入形成条件:锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待使用后释放锁即可

形成条件:队列和锁

synchronized:同步方法;锁的是this

synchronized(syncObject){//需要同步的代码
}

锁的对象就是变化的量,需要增、删、改的对象

缺陷:若将一个大的方法声明为synchronized将会影响效率

9.7、锁(Lock)

  • Java提供了更强大的线程同步机制----通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当(JDK5.0特性)
  • java.util.concurrent.locks接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、解锁
public class TestLocks {public static void main(String[] args) {Lock lock = new Lock();new Thread(lock).start();new Thread(lock).start();new Thread(lock).start();}
}class Lock implements Runnable {private int ticketNum = 10;     //票数//定义lock锁private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {try {lock.lock();       //加锁if (ticketNum > 0) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}} else {break;}System.out.println(ticketNum--);} finally {lock.unlock();         //解锁}}}
}

synchronized 与 Lock 的对比

  • Lock是显式锁(需要手动开启和关闭)synchronized是隐式锁,出了作用域自动释放
  • Lock只能锁代码块,synchronized可以锁代码块和方法
  • 使用Lock锁,JVM花费较少事件来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)

9.8、线程安全的类型

方法是否同步效率比较适合场景
线程安全多线程并发共享资源
非线程安全单线程

Hashtable && HashMap

Hashtable:

  • 继承关系(实现了Map接口,Hashtable继承Dictionary类)
  • 线程安全,效率较低
  • 键和值都不允许为null

HashMap:

  • 继承关系(实现了Map接口,继承AbstractMap类)
  • 非线程安全,效率较高
  • 键和值都允许为null

10、I/O流


10.1、什么是流?

流是内存与存储设备之间传输数据的通道

水借助管道传输;数据借助流传输

10.2、流的分类

按流向区分:

  • 输入流:将<存储设备>硬盘中的内容读入到<内存>程序
  • 输出流:将<内存>程序中的内容写入到<存储设备>硬盘

按处理数据单元划分:

  • 字节流:以字节为单位,可以读写所有数据

字节流的父类(抽象类)InputStream、OutputStream

FileInputStream:字节输入流

package com.zhang.file;import java.io.FileInputStream;
import java.io.FileNotFoundException;/*** FileInputStream的使用* 文件字节输入流*/
public class FileInputStreamDemo01 {public static void main(String[] args) throws Exception {//1.创建FileInputStream,并指定文件路径FileInputStream fis = new FileInputStream("songs.txt");//2.读取文件//2.1、单个字节读取//int data = 0;//while ((data = fis.read()) != -1) {//    System.out.println((char) data);//}//2.2、一次读取多个字节byte[] buffer = new byte[1024];int count = 0;while ((count = fis.read(buffer)) != -1) {System.out.println(new String(buffer, 0, count));}//关闭fis.close();System.out.println("\n********************");System.out.println("读取完毕!!!");}
}

OutInputStream:字节输出流

package com.zhang.file;import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;/*** FileOutPutStream的使用* 文件字节输出流*/
public class FileOutPutStreamDemo01 {public static void main(String[] args) throws Exception {//1.创建文件字节输出流对象     第二个参数指的是是否追加文本内容FileOutputStream fos = new FileOutputStream("diary.txt", true);//2.写入文件  a、97  b、98  c、99  阿斯克码表//fos.write(97);//fos.write('b');//fos.write('c');String character = "hello world!!!";fos.write(character.getBytes());//3.关闭流fos.close();System.out.println("文件创建成功!");}
}

ASCII码对照表

使用文件字节流实现文件的复制

package com.zhang.file;import java.io.FileInputStream;
import java.io.FileOutputStream;/*** 使用文件字节流实现文件的复制*/
public class FileCopyDemo01 {public static void main(String[] args) throws Exception {//创建流//1.1、文件字节输入流FileInputStream fis = new FileInputStream("01.jpg");//1.2、文件字节输出流FileOutputStream fos = new FileOutputStream("02.jpg");//2.一边读、一边写byte[] buffer = new byte[1024];//保存实际读取的字节个数int count = 0;while ((count = fis.read(buffer)) != -1) {fos.write(buffer, 0, count);}//3.关闭流fis.close();fos.close();System.out.println("复制完成!!!");}
}

使用字节缓冲流写入文件

package com.zhang.file;import java.io.BufferedOutputStream;
import java.io.FileOutputStream;/*** 使用字节缓冲流写入文件* BufferedOutPutStream的使用*/
public class BufferedOutPutStreamDemo01 {public static void main(String[] args) throws Exception {//1、创建字节输出缓冲流FileOutputStream fos = new FileOutputStream("buffer.txt");BufferedOutputStream bos = new BufferedOutputStream(fos);//2、写入文件for (int i = 0; i < 10; i++) {bos.write("hello world!\n".getBytes());       //写入8kb的缓冲区,bos.flush();        //刷新到硬盘}//3.关闭流(内部会调用flush方法)bos.close();}
}
  • 字符流:以字符为单位,只能读写文本数据

字符编码:

  • IS0-8859-1收录除ASCII外,还包括西欧、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号
  • UTF-8:针对UniCode码表的可变长度字符编码
  • GB2312:简体中文
  • GBK:简体中文、扩充
  • BIG5:台湾,繁体中文

注:当编码的方式和解码的方式不一致时,会出现乱码问题

字符流的父类(抽象类)Reader、Writer

FileReader:字符输入流

package com.zhang.file;import java.io.FileReader;
import java.io.InputStreamReader;/*** 使用FileReader读取文件*/public class FileReaderDemo01 {public static void main(String[] args) throws Exception {//1.创建文件字符输入流FileReader fr = new FileReader("study.txt");//2.读取文件//2.1、单个字符读取//int data = 0;//while ((data = fr.read()) != -1) {      //读取一个字符,不再是字节//    System.out.print((char) data);//}char[] buffer = new char[1024];int count = 0;while ((count = fr.read(buffer)) != -1) {System.out.println(new String(buffer, 0, count));}//3.关闭流fr.close();}
}

Writer:字符输出流

package com.zhang.file;import java.io.FileWriter;/*** 使用FileWriter写入文件*/
public class FileWriterDemo01 {public static void main(String[] args) throws Exception {//1.创建文件字符输出流FileWriter fw = new FileWriter("note.txt");//2.写入文件for (int i = 0; i < 10; i++) {fw.write("我热爱Java编程语言!!!\n");fw.flush();}//关闭流fw.close();System.out.println("创建文件成功!");}
}

使用文件字符流实现文件的复制

package com.zhang.file;import java.io.*;/*** 使用文件字符流实现文件的复制* 注:只能复制文本文件,不能复制图片或二进制文件* 解决方法:使用字节流可以复制任意文件*/
public class FileCopyDemo02 {public static void main(String[] args) throws Exception {//创建流//1.1文件字符输入流FileReader fr = new FileReader("note.txt");//1.2文件字符输出流FileWriter fw = new FileWriter("note03.txt");//2.读写int data = 0;char[] buffer = new char[1024];while ((data = fr.read(buffer)) != -1) {fw.write(buffer, 0, data);fw.flush();}//3.关闭流fw.close();fr.close();System.out.println("copy完成!");}
}

字符缓冲流:BufferedReader/BufferedWriter

  • 高效读写
  • 支持输入换行符
  • 可一次写一行,读一行

字符缓冲流的父类(抽象类)Reader、Writer

BufferedReader:字符缓冲输入流

package com.zhang.file;import java.io.BufferedReader;
import java.io.FileReader;/*** 使用字符缓冲输入流读取文件* BufferedReader的使用*/public class BufferedReaderDemo01 {public static void main(String[] args) throws Exception {//1.创建字符缓冲输入流FileReader fr = new FileReader("note03.txt");BufferedReader br = new BufferedReader(fr);//2.1读取文件  第一种方式//char[] buffer = new char[1024];//int data = 0;//while ((data = br.read(buffer)) != -1) {//    System.out.print(new String(buffer, 0, data));//}//2.2、一行一行读String line = null;while ((line = br.readLine()) != null) {System.out.println(line);}//3.关闭流br.close();;}
}

BufferedReader:字符缓冲输出流

package com.zhang.file;import java.io.BufferedWriter;
import java.io.FileWriter;public class BufferedWriterDemo01 {public static void main(String[] args) throws Exception {//1.创建字符缓冲输入流FileWriter fw = new FileWriter("ana.txt");BufferedWriter bw = new BufferedWriter(fw);//2.写入文件for (int i = 0; i < 10; i++) {bw.write("Strong relationships need honesty.\t\t只有以诚相待,关系才会更加牢固。");bw.newLine();       //写入一个换行符 window  \r  \n  linux  \nbw.flush();}//关闭流bw.close();System.out.println("创建成功!");}
}

打印流:PrintWriter

  • 封装了print()/println()方法,支持写入后换行
  • 支持数据原样打印
package com.zhang.file;import java.io.PrintWriter;/*** PrintWriter的使用*/
public class PrintWriterDemo01 {public static void main(String[] args) throws Exception {//1.创建打印流PrintWriter pw = new PrintWriter("print.txt");//2.打印方法pw.println(97);pw.println(true);pw.println(3.14);pw.println("b");	//数据是什么就是什么//3.关闭流pw.close();System.out.println("执行完成!");}
}

桥转换流:InputStreamReader/OutputStreamWriter

  • 可将字节流转换为字符流
  • 可设置字符的编码方式

InputStreamReader

package com.zhang.file;import java.io.FileInputStream;
import java.io.InputStreamReader;/*** InputStreamReader读取文件,指定编码*/public class InputStreamReaderDemo01 {public static void main(String[] args) throws Exception {//1.创建InputStreamReader对象FileInputStream fis = new FileInputStream("songs.txt");InputStreamReader isr = new InputStreamReader(fis, "utf-8");	//可指定编码格式//2.读取文件char[] buffer = new char[1024];int data = 0;while ((data = isr.read(buffer)) != -1) {System.out.print(new String(buffer, 0, data));}//3.关闭流isr.close();System.out.println("读取完成!");}
}

OutputStreamWriter

package com.zhang.file;import java.io.FileOutputStream;
import java.io.OutputStreamWriter;/*** 使用OutputStreamWriter写入文件,使用指定编码*/
public class OutputStreamWriterDemo01 {public static void main(String[] args) throws Exception {//1.创建OutputStreamWriter对象FileOutputStream fos = new FileOutputStream("ana02.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");     //可指定编码格式//2.写入文件for (int i = 0; i < 10; i++) {osw.write("Bend down and climb up\t\t向下弯腰向上攀升!!!\n");osw.flush();}//3.关闭流osw.close();System.out.println("创建成功!!!");}
}

按功能:

  • 节点流:具有实际传输数据的读写功能
  • 过滤流:在节点流的基础之上增强功能

10.3、对象流

对象流:ObjectOutputStream/ObjectIntputStream

  • 增强了缓冲区功能
  • 增强了读写8种基本数据类型和字符串功能
  • 增强了读写对象的功能
    • readObject()从流中读取一个对象
    • writeObject()向流中写入一个对象

使用流传输对象的过程称为序列化、反序列化

使用ObjectOutputStream实现对象的序列化

package com.zhang.file;import com.zhang.pojo.Student;import java.io.FileOutputStream;
import java.io.ObjectOutputStream;/*** 使用ObjectOutPutStream实现对象的序列化* 要求:序列化的类必须要实现Serializable接口*/
public class ObjectOutPutStreamDemo01 {public static void main(String[] args) throws Exception {//1.创建对象流FileOutputStream fos = new FileOutputStream("stu.bin");ObjectOutputStream oos = new ObjectOutputStream(fos);//2.序列化(写入操作)Student student = new Student("张三", 18);oos.writeObject(student);//3.关闭oos.close();System.out.println("序列化完毕!!!");}
}

使用ObjectInputStream实现反序列化

package com.zhang.file;import com.zhang.pojo.Student;import java.io.FileInputStream;
import java.io.ObjectInputStream;/*** 使用ObjectInputStream实现反序列化*/
public class ObjectInputStreamDemo01 {public static void main(String[] args) throws Exception {//1.创建对象流FileInputStream fis = new FileInputStream("stu.bin");ObjectInputStream ois = new ObjectInputStream(fis);//2.反序列化(读取操作)Student o = (Student) ois.readObject();//3.关闭ois.close();System.out.println("反序列化完成!!!");System.out.println(o.toString());}
}

序列化与反序列化注意事项:

  1. 序列化的类必须要实现Serializable接口
  2. 序列化类中对象属性要求实现Serializable接口
  3. 序列化版本号ID,保证序列化的类和反序列化的类是同一个类
  4. 使用transient(瞬间的)修饰属性,这个属性就不能序列化了
  5. 静态属性不能序列化
  6. 序列化多个对象,可以借助集合来实现

10.4、File类

什么是文件?

文件可认为是相关记录或放在一起的数据的集合,代表物理盘符中的一个文件或者文件夹

File类常用方法

方法概述
boolean createNewFile()创建名称的空文件,不创建文件夹
boolean mkdir()创建一个空目录
boolean delete()删除文件或空目录
boolean delete()判断文件或目录是否存在
String getAbsolutePath()获取文件的绝对路径名
String getName()获取文件的名称
String getParent()获取文件/目录所在的目录
boolean isDirectory()判断是否是目录
boolean isFile()判断是否是文件
long length()获取文件的长度/大小,单位为字节,如果文件不存在,返回0L
File[] listFiles()列出目录中的所有内容
boolean renameTo()修改文件名为xxx

文件操作

package com.zhang.file;import java.io.File;
import java.util.Date;/*** File类的使用* (1)分隔符* (2)文件操作* (3)文件夹操作*/public class FileDemo01 {public static void main(String[] args) throws Exception {//separator();//fileOpe();directoryOpe();}//分隔符public static void separator() {System.out.println("路径分隔符" + File.pathSeparator);System.out.println("名称分隔符" + File.separator);}//文件操作public static void fileOpe() throws Exception {//1.创建文件    createNewFile()File file = new File("file.txt");//System.out.println(file);if (!file.exists()) {     //如果这个文件不存在就创建该文件boolean newFile = file.createNewFile();System.out.println("创建结果:" + newFile);}//2.删除文件//2.1、直接删除//if (file.exists()) {    //如果这个文件存在就删除该文件//    System.out.println("删除结果:" + file.delete());//}//2.2、使用jvm退出时删除文件//Thread.sleep(5000);//file.deleteOnExit();//3.获取文件信息System.out.println("=====文件信息=====");System.out.println("获取文件绝对路径:" + file.getAbsolutePath());System.out.println("获取文件路径:" + file.getPath());System.out.println("获取文件名称:" + file.getName());System.out.println("获取文件父目录:" + file.getParent());System.out.println("获取文件长度:" + file.length());System.out.println("文件创建时间:" + new Date(file.lastModified()).toLocaleString());//4.判断System.out.println("是否是目录?" + file.isDirectory());System.out.println("是否是文件?" + file.isFile());System.out.println("是否可读?" + file.canRead());System.out.println("是否可写?" + file.canWrite());System.out.println("是否隐藏?" + file.isHidden());System.out.println("是否是绝对路径?" + file.isAbsolute());}public static void directoryOpe() throws Exception {//1.创建文件夹File dir = new File("E:\\study\\note\\chapter");System.out.println(dir);if (!dir.exists()) {     //如果不存在就创建//dir.mkdir();    //只能创建单级目录System.out.println("创建结果:" + dir.mkdirs());       //创建多级目录}//2.删除文件夹//2.1、直接删除//if (dir.exists()) {    //如果这个目录存在就删除该目录   注:(只能删除空目录)//    System.out.println("删除结果:" + dir.delete());//}//2.2、使用jvm退出时删除文件//Thread.sleep(5000);//dir.deleteOnExit();//3.获取文件夹信息System.out.println("=====文件夹信息=====");System.out.println("获取绝对路径:" + dir.getAbsolutePath());System.out.println("获取路径:" + dir.getPath());System.out.println("获取文件夹名称:" + dir.getName());System.out.println("获取父目录:" + dir.getParent());System.out.println("目录创建时间:" + new Date(dir.lastModified()).toLocaleString());//4.判断System.out.println("是否是目录?" + dir.isDirectory());System.out.println("是否是文件夹?" + dir.isFile());System.out.println("是否隐藏?" + dir.isHidden());System.out.println("是否是绝对路径?" + dir.isAbsolute());//5.遍历文件夹File dir2 = new File("E:\\study\\note\\chapter");String[] files = dir2.list();       //获取该文件夹下的文件信息for (String file : files) {System.out.println(file);}}
}

FileFilter接口

public interface FileFilter

  • boolean accept(File pathname)

当调用File类中的listFiles()方法时,支持传入FileFilter接口实现类,对获取文件进行过滤,只有满足条件的文件才可出现在listFiles()的返回值中。

package com.zhang.file;import java.io.File;
import java.io.FileFilter;/*** FileFilter的使用,实现对文件的过滤*/
public class FileFilterDemo01 {public static void main(String[] args) {directoryOpe(new File("E:\\study\\note\\chapter"));}public static void directoryOpe(File fileName) {File[] files = fileName.listFiles(pathname -> {if (pathname.getName().endsWith(".jpg")) return true;   //如果文件名后缀为.jpg(满足该条件)就返回truereturn false;});for (File file1 : files) {System.out.println(file1.getName());}}
}

递归遍历文件夹和递归删除文件夹

package com.zhang.file;import java.io.File;/*** 实现递归遍历文件夹和递归删除文件夹* 不能删除空目录*/
public class ListDirDemo01 {public static void main(String[] args) {//listDir(new File("E:\\study\\note"));delDir(new File("E:\\study\\note"));}//1.1、递归遍历文件夹public static void listDir(File dir) {File[] files = dir.listFiles();System.out.println(dir.getAbsolutePath());if (files != null && files.length > 0) {for (File file : files) {if (file.isDirectory()) listDir(file);      //递归else System.out.println(file.getAbsolutePath());}}}//1.2、递归删除文件夹public static void delDir(File dir) {File[] files = dir.listFiles();if (files != null && files.length > 0) {for (File file : files) {if (file.isDirectory()) delDir(file);    //递归else System.out.println(file.getAbsolutePath() + "删除结果:" + file.delete());       //先删除文件,再删除文件夹}System.out.println(dir.getAbsolutePath() + "删除结果:" + dir.delete());       //先删除文件,再删除文件夹}}
}

11、常用类


12、反射


12.1、什么是类对象?

类的对象:基于某个类new出来的对象,也称为实例对象

类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法…),每个类加载到内存中后都对应一个Class对象,每个类有且只有一个Class对象

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

12.2、获取类对象

  1. 通过类的对象,获取类对象

    //语法:类名.getClass();
    Person zhangSan = new Person("张三", 18);
    Class<? extends Person> zhangSanClass = zhangSan.getClass();
    System.out.println(zhangSanClass.hashCode());
    
  2. 通过类名获取类对象

    //语法:Class c = 类名.class;
    Class<Person> personClass = Person.class;
    System.out.println(personClass.hashCode());
    
  3. 通过静态方法获取类对象

    //语法:Class<?> c = Class.forName("包名.类名");
    Class<?> c = Class.forName("com.zhang.reflect.Person");
    System.out.println(c.hashCode());
    

反射常用操作

package com.zhang.reflect;public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;System.out.println("带参构造执行!");}public Person() {System.out.println("无参构造执行!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void eat() {System.out.println(name + "吃东西!");}//带参方法public void eat(String food) {System.out.println(name + "开始吃:" + food);}//私有方法private void privateMethod() {System.out.println("这是一个私有方法!");}//静态方法public static void staticMethod() {System.out.println("这是一个静态方法!");}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
package com.zhang.reflect;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;public class TestPerson {public static void main(String[] args) throws Exception {//Person zhangSan = new Person("张三", 18);//zhangSan.eat();//reflectOpe1();//reflectOpe2();//reflectOpe3();reflectOpe4();Properties properties = new Properties();//properties.setProperty("name", "zhangSan");//System.out.println(properties);invokeAny(properties, "setProperty", new Class[]{String.class, String.class}, "userName", "张三");System.out.println(properties);}public static void getPersonClass() throws Exception {Person zhangSan = new Person("张三", 18);//1.通过类的对象,获取类对象Class<? extends Person> zhangSanClass = zhangSan.getClass();System.out.println(zhangSanClass);//2.通过类名获取类对象Class<Person> personClass = Person.class;System.out.println(personClass.hashCode());//3.通过静态方法获取类对象Class<?> c = Class.forName("com.zhang.reflect.Person");System.out.println(c.hashCode());}//1.使用反射获取类的名字、包名、父类、接口public static void reflectOpe1() throws Exception {Class<?> c = Class.forName("com.zhang.reflect.Person");System.out.println("类的名称:" + c.getName());System.out.println("包名:" + c.getPackage());System.out.println("父类:" + c.getSuperclass());System.out.println("接口:" + c.getInterfaces().hashCode());}//2.使用反射获取构造方法,创建对象public static void reflectOpe2() throws Exception {//1.获取类对象Class<?> c = Class.forName("com.zhang.reflect.Person");//2.获取类的构造方法  Constructors//Constructor<?>[] cons = c.getConstructors();//for (Constructor<?> con : cons) {//    System.out.println(con);//}//3.获取类中无参构造,创建对象Constructor<?> con = c.getConstructor();Person liSi = (Person) con.newInstance();liSi.setName("李四");System.out.println(liSi);//简易方法  类对象.newInstance();Person xiaoMing = (Person) c.newInstance();xiaoMing.setName("晓明");System.out.println(xiaoMing);//4.获取类中带参构造方法Constructor<?> con2 = c.getConstructor(String.class, int.class);Person xiaoLin = (Person) con2.newInstance("晓琳", 19);System.out.println(xiaoLin);}//3.使用反射获取类中方法,并调用方法public static void reflectOpe3() throws Exception {//1.获取类对象Class<?> c = Class.forName("com.zhang.reflect.Person");//2.获取方法,Method对象//2.1、getMethods()获取公开的方法,包括从父类继承的方法//Method[] methods = c.getMethods();//for (Method method : methods) {//    System.out.println(method);//}//2.2、getDeclaredMethods()获取类中的所有方法,包括私有、默认、保护(不包括继承的)//Method[] methods1 = c.getDeclaredMethods();//for (Method method : methods1) {//    System.out.println(method);//}//3.获取单个方法Method eatMethod = c.getMethod("eat");//调用方法//正常调用: Person zhangSan = new Person();  zhangSan.eat();System.out.println("*****调用无参方法*****");Person xiaoTian = (Person) c.newInstance();xiaoTian.setName("小天");eatMethod.invoke(xiaoTian);     //zhangSan.eat();//3.2 toStringSystem.out.println("*****调用有返回值的方法*****");Method toStringMethod = c.getMethod("toString");Object result = toStringMethod.invoke(xiaoTian);System.out.println(result);//3.3 eatMethod 带参System.out.println("*****调用带参方法*****");Method eatMethod1 = c.getMethod("eat", String.class);       //调用的方法名称  参数类型eatMethod1.invoke(xiaoTian, "烧鸡");      //调用方法的对象  传递的参数//3.4 获取私有方法Method privateMethod = c.getDeclaredMethod("privateMethod");privateMethod.setAccessible(true);//调用私有方法必须设置访问权限无效  否则报错:IllegalAccessException(访问权限的异常)  Class com.zhang.reflect.TestPerson can not access a member of class com.zhang.reflect.Person with modifiers "private"privateMethod.invoke(xiaoTian);//3.4、获取静态方法Method staticMethod = c.getMethod("staticMethod");//正常调用  Person.staticMethod();staticMethod.invoke(null);}//4.使用反射实现一个可以调用任何对象方法的通用方法public static Object invokeAny(Object obj, String methodName, Class<?>[] types, Object... args) throws Exception {//1.获取类对象Class<?> c1 = obj.getClass();//2.获取方法Method method = c1.getMethod(methodName, types);//3.调用return method.invoke(obj, args);}//5.使用反射获取类中的属性public static void reflectOpe4() throws Exception {//1.获取类对象Class<?> c = Class.forName("com.zhang.reflect.Person");//2.获取属性(字段)公开的字段、父类继承的字段//Field[] fields = c.getFields();//for (Field field : fields) {//    System.out.println(field);//}//2.1、getDeclaredFields()获取类中所有的属性,包括私有、保护、默认,(不包含继承的)Field[] allFiled = c.getDeclaredFields();for (Field field : allFiled) {System.out.println(field);}//3.获取单个属性Field nameFiled = c.getDeclaredField("name");//3.1、赋值  3.2、获取值// 正常调用  Person xiaoKun = new Person();   xiaoKun.setName();Person xiaoKun = (Person) c.newInstance();//必须给私有属性设置访问权限无效 否则报错:IllegalAccessException(访问权限的异常)   Class com.zhang.reflect.TestPerson can not access a member of class com.zhang.reflect.Person with modifiers "private"nameFiled.setAccessible(true);nameFiled.set(xiaoKun, "小坤");     //xiaoKun.setName("小坤");System.out.println(nameFiled.get(xiaoKun));  //xiaoKun.getName();}
}

12.3、设计模式

什么是设计模式?

一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,简而言之来说就是特定问题的固定解决方法

好处:使用设计模式为了可重用代码、让代码更容易被他人理解、增强代码的可靠性、重用性

GOF

工厂设计模式

  • 工厂设计模式主要负责对象的创建
  • 开发中有一个非常重要的原则“开闭原则”,对拓展开放、对修改关闭
  • 可通过反射进行工厂模式的设计,完成动态的对象创建
http://www.lbrq.cn/news/1034605.html

相关文章:

  • 网站开发 安全合同/app开发制作
  • 如何做网站地图视频/关键词seo优化软件
  • 如何查网站空间/漂亮的网页设计
  • 做网站 要学 什么语言/seo搜索引擎优化课后答案
  • 中山蚂蚁网站开发/百度推广平台有哪些
  • 工程建设标准最新查询网站/小红书kol推广
  • 信息发布的网站怎么做/营业推广策略有哪些
  • 做网站用微软雅黑侵权吗/视频营销模式有哪些
  • 德令哈网站建设公司/自主建站
  • 网站ppt怎么做/什么是网络营销公司
  • 做宴会有哪些素材网站/郑州互联网公司排名
  • 万网域名指向网站/百度网站客服
  • 杭州市规划建设委员会网站/大概需要多少钱
  • 中国政府网站建设与应用/360网站关键词排名优化
  • 务川县住房和城乡建设局网站/百度云官网首页
  • 网站htm建设/seo实战教程
  • 微店网页版登录入口/seo网站优化工具大全
  • wordpress demo data/汕头自动seo
  • 中国纪检监察网站奶奶做女工/网站优化外包找谁
  • 恶搞网站怎么做/网站怎么收录
  • 新密网站/软文营销的案例
  • 东海县建设局网站/成都seo达人
  • 东营建设信息网站/谷歌seo服务
  • 微信公众号建设公司/安卓优化大师官网下载
  • 全响应式网站用什么做的/搜索引擎营销的主要模式有哪些
  • 吉林省住房和城乡建设厅网站6/外链怎么打开
  • 网站出现风险如何处理/seo双标题软件
  • 做网站怎样做才有百度快照/app开发流程
  • 网页设计旅游网站前言/快速学电脑培训班
  • 网站空间价格/郑州网站建设最便宜
  • 数据分析项目----幸福感挖掘和预测
  • 虚拟机一站式部署Claude Code 可视化UI界面
  • 支持任意 MCP 协议的客户端
  • B 树与 B + 树解析与实现
  • 验证二叉搜索树
  • 强化学习常用数据集