面向过程思想
面向对象编程
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
抽象:编程思想!
面向对象三大特征
从认识论角度考虑是先有对象后有类。类,是抽象的,它是对象的抽象。而对象,是具体的事物。
从代码运行角度考虑是先有类后有对象。类是对象的模板
类:
静态的属性 属性
动态的行为 方法
属性(property):字段(Field) 成员变量
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
语法:修饰符 属性类型 属性名 = 属性值;
public String name = "张三";
public int name = 18;
public boolean adult = false; //是否是成年人
使用new
关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象,进行默认的初始化,以及对类中构造器的调用
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
构造方法的作用:主要用来初始化对象的值
注:一旦定义了有参构造,系统不再提供默认无参构造方法,无参就必须显示定义
//一个类即使什么也不行,它也会存在一个方法(无参构造)
//显示定义构造器
public Person(){
}
//实例化初始值 (有参构造)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
this关键字是对一个对象的默认引用(当前类的)
对象的引用:对象是通过引用来操作的:栈–>堆
封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问(隐藏内部细节,保留对外接口)
我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
记住这句话就够了:属性私有,get/set,方法公开
封装的步骤:
//属性私有
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;
}
}
封装的好处
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
未使用继承前
继承是代码重用的一种方式
将子类共有的属性和行为放到父类中
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
修饰的类不能再被继承
super关键字可以在子类访问父类方法,进而完成在子类的复用,super代表父类对象
注:
super VS this
代表的对象不同:
前提:
构造方法:
注:this与super都要求在构造方法的首行,所以两者不能同时使用
方法重写的规则:
重写:子类的方法和父类必须一致;方法体不同
//父类
//重写指的是方法的重写,跟属性无关
public class B {
public void test(){
System.out.println("B=>test");
}
}
//子类
public class A extends B{
@Override
public 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
}
}
为什么需要重写?
父类的功能,子类不一定需要,或者不一定满足
访问修饰符
生活中的多态:同一种操作,由于条件不同,产生的结果也不同
程序中的多态:同一个引用类型,使用不同的实例而执行不同操作(父类引用指向子类对象),从而产生多种形态
多态存在的条件:
注:多态是方法的多态,属性没有多态
父类和子类,有联系,类型装换异常!ClassCastException
不能被重写的方法:
多态的应用
场景一:使用父类作为方法形参实现多态,使方法参数的类型更为宽泛
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{
@Override
public void eat() {
System.out.println("猫咪正在吃猫粮!");
}
public void run() {
System.out.println("猫咪正在跑!");
}
}
package com.zhang.polymorphic;
public class Bird extends Animal {
@Override
public 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("购买失败!");
}
}
}
多态
//abstract 抽象类:类 extends:单继承~ (接口可以多继承)
public abstract class Action {
//abstract 抽象方法,只有方法名字,没有具体的方法实现
//约束~换而言之就是有人帮我们实现
public abstract void doSomething();
}
//抽象类的所有方法,只要其它子类继承了它,都必须要实现它的方法~ 除非子类也是抽象的
public class A extends Action{
@Override
public void doSomething() {
}
}
抽象类的特点:
抽象方法的特点:
抽象类存在的意义:提高开发效率,提高程序的可扩展性
定义接口使用interface关键字
//所有方法默认都是:public abstract
public interface MyInterface {
public void foo();
//其他方法
}
接口:只有规范!自己无法写方法~专业的约束!约束和实现分离:面向接口编程
接口的本质是契约,就像我们社会的法律一样,制定好大家都遵守
OOP的精髓,是对对象的抽象,最能体现这一点就是接口。为什么讨论设计模式,都只针对能力的语言(比如java、c++、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象(架构师)
微观概念:接口表示一种能力
接口的定义:代表了某种能力
方法的定义:能力的具体要求
接口与抽象类的异同
相同点:
不同点:
程序设计时:
例:
防盗门是一个门 (is a的关系)
防盗门是有一个锁 (has a的关系)那么上锁、开锁就是它的能力
定义接口
//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();
}
实现接口
//类 可以实现接口!使用implement关键字实现接口
//实现类接口的类,就需要重写接口中的方法
//多继承~利用接口实现伪多继承! 可为类扩充多种能力
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void del(String name) {
}
@Override
public void update(String name) {
}
@Override
public void check(String name) {
}
@Override
public void time() {
}
使用接口
public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.add("小张");
}
}
常见的关系
类与类:
类与接口:
回调原理
使用接口的好处:
常量接口将多个常用于表示状态或固定值的变量,以静态常量的形式定义在接口中统一管理,提高代码可读性。
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");
}
}
}
标记接口中没有包含任意成员,仅仅用作标记
内部类就是在一个类的内部又定义了一个类,比如,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(){
@Override
public void hello() {
}
};
}
}
注:不推荐使用
生活中的异常:
正常情况下,小张每天开车去上班,耗时大约30分钟
程序中的异常:
异常指程序运行中出现的不期而至的各种状况,如:文件找不到、网络连接失败、接收非法参数等
异常处理的必要性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失
检查时异常(CheckedException):就是编译器要求你必须处理的异常。比如我们在编程某个文件的读/写时,编译器要求你必须要对某段代码try…catch… 或者 throws exception,这就是检查异常,简单的来说,你代码还没有运行,编码器就会检查你的代码,对可能出现的异常必须做出相对的处理。(比如当文件不存在时…)
运行时异常(RuntimeException):运行时异常是不需要捕获的,程序员可以不去处理,当异常出现时,虚拟机会处理。常见的运行时异常有空指针异常等
错误error:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有错误和异常的超类。
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 | 并发修改异常 |
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
Error和Exception的区别:
Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;
Exception通常情况下是可以被程序处理的,并且在程序中应尽可能的去处理这些异常
异常的传递
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 { //处理善后工作 最终都会执行,除非手动退出JVM
System.out.println("程序结束!");
}
//finally,可以不要finally,假设I/O,资源,关闭,就需要使用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);
}
如果在一个方法体中抛出了异常,如何通知调用者?
使用原则:底层代码向上声明或者抛出异常,最上层一定要处理异常,否则程序中断
抛出异常:
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);
}
}
}
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception
类即可!
使用自定义异常类,大体分为以下几个步骤:
package com.zhang;
public class Person {
private String gender;
public Person(String gender) {
this.gender = gender;
}
public Person() {
}
@Override
public 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:性别不符合要求!
注:子类中的方法,不能抛出比父类更多、更宽的检查时异常
经验:
printStackTrace()
去打印输出例:存储一个班学员信息,假定一个班容纳20名学员,我们可以使用数组存储
如何存储每天的新闻信息?每天的新闻总数不确定,太少浪费空间,太多空间不足
如果并不知道程序运行时会需要多少对象,或者需要更复杂方式存储对象——可以使用Java集合框架
集合和数组的区别:
Collections:提供了对集合进行排序、遍历等多种算法实现
Collection 接口存储一组唯一(元素不可以重复),无序的对象
List 接口存储一组不唯一(元素可以重复),有序(无序是指存入元素的先后顺序与输出元素的先后顺序不一致)的对象
Set 接口存储一组唯一(元素不可重复),无序的对象
Map接口用于存储任意键值对,提供key到value的映射
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 code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int 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());
}
}
}
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() {
}
@Override
public int hashCode() {
int n1 = this.name.hashCode();
int n2 = this.age + 31; //(1) 31是一个质数(素数),只能被1和它自身整除的,减少散列冲突 (2)提供执行效率31*i=(i<<5)-i
return n1 + n2;
}
@Override
public 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.条件都不满足返回false
return false;
}
@Override
public 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 否则为false
System.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() {
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//先按照姓名比较 ,然后再按年龄比较
@Override
public 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>() {
@Override
public 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值。
Map接口专门处理键值映射数据的存储,可以根据键实现对值的操作
Map接口的实现类:HashMap(基于哈希表实现)和TreeMap(基于红黑树(二叉查找树)实现的)
HashMap通过hashcode对其内容进行快速查找,允许用null作为key或是value,运行效率快、线程不安全(JDK1.2)
TreeMap中所有的元素都保持着某种固定的顺序,实现了SortedMap接口(是Map的子接口),可以对key自动排序,运行效率快、线程不安全(JDK1.2)
HashTable
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 keySet = map.keySet();
for (String countryKey : map.keySet()) {
System.out.println(countryKey + "\t\t" + map.get(countryKey));
}
//3.2、使用entrySet()方法遍历 entrySet效率要高于keySet
System.out.println("=====使用entrySet()方法遍历=====");
//Set> 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 students = hashMap.keySet();
for (Student student : hashMap.keySet()) {
System.out.println(student + "\t\t" + hashMap.get(student));
}
//3.2、使用entrySet()方法遍历 entrySet效率要高于keySet
System.out.println("=====使用entrySet()方法遍历=====");
//Set> 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; //元素个数
总结:
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>() {
@Override
public 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效率要高于keySet
System.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());
}
}
语法:
<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 二分查找器 找到集合中的元素下标 找不到返回-1
int 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.UnsupportedOperationException
List<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);
}
}
//使用迭代器遍历输出 iterator
Set<String> keys = countryMap.keySet(); //取出所有key的集合
Iterator<String> ctIterator = keys.iterator(); //获取Iterator对象
while (ctIterator.hasNext()) {
String key = ctIterator.next(); //取出key
Country 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());
}
继承自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);
}
}
在了解多线程之前,需要先了解多任务、线程、进程这几个名词
所谓多任务就是在同一时间做多种事情,例如window系统的任务管理器
在操作系统中运行的程序就是进程,比如QQ、播放器、IDE、游戏等
一个进程可以有多个线程,如视频中同时听到声音、看见图像、弹幕等
如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程,多个线程交替占用CPU资源,而非真正的并行执行
线程是CPU调度和执行的单位
普通方法调用和多线程
继承java.lang.Thread类
//创建线程方式一:继承Thread类 重写run方法,调用start开启线程
public class MyThread extends Thread {
@Override
public 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调度执行
实现java.lang.Runnable接口
//龟兔赛跑
public class Race implements Runnable {
private static String winner; //获胜者
@Override
public 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类
实现Runnable接口
注:多个线程操作统一共享资源时,将引发数据不安全问题
//静态代理模式:真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:代理对象可以做很多真实对象做不了的事情
//真实对象专注做自己的事情
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{
@Override
public void happyMarry() {
System.out.println("张先生要结婚了!真开心");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
//代理谁--> 真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target; //这是真实对象
}
@Override
public void happyMarry() {
before();
this.target.happyMarry();
after();
}
private void before() {
System.out.println("结婚之前,布置现场");
}
private void after() {
System.out.println("结婚之前,收尾款");
}
}
线程方法
方 法 | 说 明 |
---|---|
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;
@Override
public 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();
}
}
}
}
为什么需要线程同步?
多个线程操作同一共享资源时,将引发数据不安全问题
并发:同一个对象被多个线程同时操作
现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决方法就是:排队一个一个来
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程在使用
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入形成条件:锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待使用后释放锁即可
形成条件:队列和锁
synchronized:同步方法;锁的是this
synchronized(syncObject){
//需要同步的代码
}
锁的对象就是变化的量,需要增、删、改的对象
缺陷:若将一个大的方法声明为synchronized将会影响效率
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();
@Override
public 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 的对比
synchronized
可以锁代码块和方法方法是否同步 | 效率比较 | 适合场景 | |
---|---|---|---|
线程安全 | 是 | 低 | 多线程并发共享资源 |
非线程安全 | 否 | 高 | 单线程 |
Hashtable && HashMap
Hashtable:
HashMap:
水借助管道传输;数据借助流传输
按流向区分:
硬盘
中的内容读入到<内存>程序
中程序
中的内容写入到<存储设备>硬盘
中按处理数据单元划分:
字节流的父类(抽象类)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();
}
}
字符编码:
注:当编码的方式和解码的方式不一致时,会出现乱码问题。
字符流的父类(抽象类)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 \n
bw.flush();
}
//关闭流
bw.close();
System.out.println("创建成功!");
}
}
打印流:PrintWriter
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("创建成功!!!");
}
}
按功能:
对象流:ObjectOutputStream/ObjectIntputStream
使用流传输对象的过程称为序列化、反序列化
使用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());
}
}
序列化与反序列化注意事项:
什么是文件?
文件可认为是相关记录或放在一起的数据的集合,代表物理盘符中的一个文件或者文件夹
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
当调用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(满足该条件)就返回true
return 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()); //先删除文件,再删除文件夹
}
}
}
类的对象:基于某个类new出来的对象,也称为实例对象
类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法…),每个类加载到内存中后都对应一个Class对象,每个类有且只有一个Class对象
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
通过类的对象,获取类对象
//语法:类名.getClass();
Person zhangSan = new Person("张三", 18);
Class<? extends Person> zhangSanClass = zhangSan.getClass();
System.out.println(zhangSanClass.hashCode());
通过类名获取类对象
//语法:Class c = 类名.class;
Class<Person> personClass = Person.class;
System.out.println(personClass.hashCode());
通过静态方法获取类对象
//语法: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("这是一个静态方法!");
}
@Override
public 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 toString
System.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();
}
}
什么是设计模式?
一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,简而言之来说就是特定问题的固定解决方法
好处:使用设计模式为了可重用代码、让代码更容易被他人理解、增强代码的可靠性、重用性
GOF
工厂设计模式