文章目录
一、面向对象三大特征
1.封装
2.继承
1)私有属性也可以被继承 但是不可以直接调用
2)所有的类都直接或间接的继承于Object类
3)方法的重写(@Override):
子类继承父类以后,可以对父类中同名同参数的方法进行覆盖操作
应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参的方法时,实际执行的是子类重写父类的方法。
4)super关键字
1.super理解为父类的“”,对比与this来看。可以用来调用属性,方法,构造器
2.我们可以在子类的方法或构造器中,通过super.属性或super.方法的方式显示的调用父类中声明的属性或方法。但是通常情况下我们省略“super”
3.当子类和父类定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显示的使用“super.属性”的方式
4.当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用“super.方法”
5.我们可以在子类的构造器中现实的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器,“super(形参列表)”的使用,必须声明在构造器的首行。我们在类的构造器中,针对于“super(形参列表)”或“super’(形参列表)” 只能二选一,不能同时出现
6) instanceof关键词的使用
a instanceof A 判断对象a是否是类A的实例,如果是 返回true 不是 返回false
7) object类中的功能具有通用性
8) 只声明一个空参构造器
tostring()方法 当我们输出一个对象的引用时,实际上就是调用了当前对象的tostring()
抽象类和接口的使用肯定体现了多态性 (抽象类,接口不能实例化)
数组作为object类的子类出现,可以调用object类中国声明的方法
9) toString
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
3.多态
二、接口
1.接口使用interface定义 和类是并列的结构
2.JDK7之前,只能定义全局常量和抽象方法,(public static final的)和(public abstract)书写时可以省略不写 JDK8之后还可以定义静态方法和默认方法。
3.接口中不能定义构造器,接口不可实例化
4.Java开发中,接口都通过让类去实现(implements)的方式来使用,如果实现类覆盖了接口中的所有抽象方法,则此类可以实例化,如果没有覆盖所有抽象方法,则此类仍为一个抽象类
5.Java类可以实现多个接口,弥补了java单继承性的局限性问题。格式: class AA extends BB implements CC,DD
类和类之间:继承(只能单继承) 类和接口之间:实现 接口和接口之间:继承(可以多继承)
6.接口的实现体现了多态性,接口实际上就可以看作一种规范
7.java8中的新特性 接口中定义的静态方法,只能通过接口来调用。通过实现类的对象,可以调用接口中的方法,如果实现类重写了接口中的默认方法,调用时,仍然调用重写后的方法;如果子类(或实现类)继承的父类和实现的接口声明了同名同参的方法,那么子类在没有重写此方法的情况下,默认调用父类中的同名同参的方法(类优先原则);如果实现了多个接口,而这多个接口定义了同名同参的默认方法,那么在实现类没有重写的情况下,报错(接口冲突),这就要求我们必须在实现类中重写此方法。调用接口中的默认方法,接口名.super.方法。
三、异常
1.异常的体系结构 java.lang.Error:一般不编写针对性的代码进行处理
java.lang.Exception :可以进行异常处理
2.编译时异常:
IOException(FileNotFoundException) ClassNotFoundException
运行时异常:
NullPointerException ArrayIndexOutBoundsException ClassCastException InputMismatchExpection ArithmeticException
3.异常的处理
3.1 抓抛模型 过程一 “抛” 程序再正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常的对象并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。 过程二:“抓” 可以理解为异常的处理方式:①try-catch-finally ②throws
try-catch-finally的使用
try {
//可能出现的异常的代码
}catch(异常类型1 变量名1){
// 处理异常的方式1
}catch(异常类型2 变量名2){
// 处理异常的方式2
}catch(异常类型3 变量名3){
// 处理异常的方式3
}
finally {
//一定会执行的代码
}
使用try将可能出现的异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常的对象,根据此对象的类型,去catch中进行匹配。一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前try-catch结构(在没有写finally的情况下),继续执行其后的代码。
catch中的异常类型如果没有子父类关系,则谁声明在上谁声明在下无所谓,如果有,则子类写上面,父类写下面。
常用的异常对象处理的方式:①String getmessage()②printStackTrace()
在try中声明的变量,除了try结构,就不能再使用了
使用try-catch-finally处理编译时异常,使得程序在编译时不再报错,但是运行时仍可能报错,相当于我们使用try-catch-finally将一个可能出现的异常延迟到运行时出现。
finally是可选的,finally中声明的是一定会执行的代码,即使catch中又出现了异常
像数据库连接,输入输出流,网络编程socket等资源,jvm是不能自动回收的,我们需要自己手动进行资源的释放,此时的资源释放,就需要声明在finally中。
②“throws+异常类型“写在方法的声明处,指明此方法执行时,可能会抛出的异常类型。一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出,异常代码后面的代码就不再执行;
3.2 try-catch-finally真正的将异常给处理掉了,throws的方式只是将异常抛给了方法的调用者,并没有真正的将异常处理掉。
3.3 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
3.4 手动抛出异常 throw
3.5 手动定义异常类①继承现有的异常类结构:RuntimeException ,Exception
②提供全局常量,serialVersionUID
四、多线程
1.线程
一个Java应用程序,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。
2.多线程的创建:
(1)继承于Thread类
- 创建一个继承于Thread类的子类,
- 重写Thread类的run(),将此线程执行的操作声明在run()中;
- 创建Thread类的子类的对象,
- 通过此对象调用start() 启动当前线程,调用当前线程的run()
(2)实现Runable 接口
- 创建一个实现了Runable接口的类;
- 实现类去实现Runable中的抽象方法run();
- 创建实现类的对象;
- 将此对象作为参数传递到Thread类的构造器中;
- 通过Thread类的对象调用start()。
(3)实现Callable接口(jdk5新增的)
- 创建一个实现callable的实现类;
- 实现call(),将此线程需要执行的操作声明在call()中;
- 创建callable实现类的对象;
- 将此callable实现类的对象作为参数传递到futureTask构造器中,创建futuretask的对象
- 将futuretask的对象作为参数传递到thread类的构造器中,创建thread对象,并调用start()
- 获取callable中的call()的返回值。
功能比实现runnable接口强大:
①call()可以有返回值;
②call()可以抛出异常,被外面的操作捕获,获取异常的信息;
③callable是支持泛型的。
(4)使用线程池
- 提供指定线程数量的线程池;
- 执行指定的线程操作,需要提供实现runnable接口或callable接口实现类的对象;
- 关闭连接池
好处:
- 提高响应速度;
- 降低资源消耗;
- 便于线程管理。
(5)这两种方式创建线程的比较:开发中优先选择实现Runnable接口的方式。
原因:
①实现的方式没有类的单继承性的局限性。
②实现的方式更适合来处理多个线程有共享数据的情况。
两种方式的联系:都需要重写run(),将创建的线程要执行的逻辑声明在run()
3.问题:
我们不能通过直接调用run()的方式启动线程;不可以还让已经start()的线程去执行,我们需要重新创建一个线程的对象
4.调用Thread中的常用的方法:
(1)start():启动当前线程,调用当前线程的run()
(2)run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
(3)currentThread(): 静态方法,返回当前代码执行的线程
(4)getName():获取当前线程的名字
(5)setName():设置当前线程的名字
(6)yield():释放当前cpu执行权
(7)join():在线程a中调用线程b的join(),此时线程a就进入了阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
(8)sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒,子啊指定的时间内,此线程是阻塞状态
(9)isAlive():判断线程是否存活
5.线程优先级:
(1)MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
(2)如何获取和设置当前线程优先级 getPriority(): 获取线程的优先级 setPriority(int p):设置线程的优先级
高优先级的线程要抢占低优先级线程cpu的执行权,但是从概率上讲,高优先级的线程在高概率的情况下被执行,并不意味着只有当高优先级的线程执行完,低优先级的线程才会执行。
6.线程的生命周期
新建-》就绪-》运行-》死亡 外加阻塞
7.线程安全问题,
在Java中我们通过同步机制,来解决线程的安全问题
(1)方式一:synchronized(同步监视器){需要被同步的代码} 说明:操作共享数据的代码,即为需要被同步的代码。同步监视器:俗称 锁,任何一个类的对象都可以充当锁。要求:多个线程必须要用同一把锁。
(2)方式二:使用同步方法,如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。同步方法仍然涉及到同步检测器,只是不需要我们显示的声明,非静态的同步方法,同步监视器是this 静态的同步方法,同步监视器是类本身。
(3)同步的方式:解决了线程安全的问题(好处) 操作同步代码时,只能由一个线程参与,其他线程等待,相当于单线程的过程,效率低(局限性)
8 死锁
我们使用同步时,要避免出现死锁
9 线程通信
涉及到的三个方法:(1)wait()一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器(2)notify()一旦执行此方法,就会唤醒wait的一个线程,如果有多个线程被wait,就唤醒优先级最高的(3)notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
说明:(1)上述三个方法必须使用在同步代码块或同步方法中。(2)三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则会出现ILLegalMonitorStateException异
五、常用类
1.string类
1)特点:
①string声明为final的,不可被继承;
②string实现了Serializable接口,表示字符串时支持序列化的;
③实现了comparable接口,表示string可以比较大小;
④string内部定义了final char【】 value 用于存储字符串数据;
⑤string代表不可变的字符序列,不可变性;
⑥通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串生命在字符串常量池中;
⑦字符串常量池中是不会存储相同内容的字符串
2)实例化
方式一:通过字面量定义的方式,方式二:通过new+构造器的方式
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
3)结论
①常量与常量的拼接结果在常量池中,且常量池中不会存在相同内容的常量;
②只要其中有一个是变量,结果就在堆中;
③如果拼接的结果调用intern()方法,返回值就在常量池中;
4)常用方法1
①int length():返回字符串长度 ;
②char charAt()返回某索引处的字符;
③boolean isEmpty 判断是否是空字符串;
④string toLowerCase()转换为小写
⑤string trim()去掉字符串首尾空格;
⑥boolean equals(Object obj)比较字符串内容是否相等;
⑦boolean equalsIgnoreCase()去除大小写判断两字符串是否相等;
⑧string contact(string str) 将指定字符串连接到此字符串末尾,相当于+;
⑨int compareTo(string anotherString)比较两个字符串的大小
⑩string substring(int beginIndex)返回一个新的字符串,他是此字符串的从beginIndex开始截取的字串
string substring(int beginIndex,int endIndex)返回一个新字串,他是从beginIndex开始截取到endIndex的字串
5)常用方法2
①boolean endWith(String suffix)判断字符串是否以指定的后缀结尾;
②boolean startWith(string prefix)判断字符串是否以指定的前缀开始;
③boolean startWith(string prefix,int toffset):测试此字符串从指定索引开始的字符串是否以指定前缀开始;
④boolean contains(charSeque s):判断当前字符串是否包含指定字符串
⑤int indexOf():返回指定字符串在此字符串第一次出现的索引位置,
⑥int lastIndexOf(string str)返回指定子字符串在此字符串中最后一次出现的索引位置
6)常用方法3
①string replace(char oldChar,char newChar):返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有的oldChar得到的
②string replace(charSequence target,charSequence replacement);使用指定的字面值替换此字符串所有匹配字面值目标序列的子字符串
③string replaceAll(string regex,string replace)
④string split():切分
7)string类与其他结构之间的转换
string与char的转换:string-》char【】调用string的toCharSting
char【】 charArray = str1.toCharArray();
char【】-》string 调用string的构造器
string与byte【】的转换
string-》byte【】:调用string的getBytes()
byte-》string:调用string的构造器
2.StringBuffer
string stringBuffer stringBuiler三者的异同
string:不可变的字符序列,底层使用char[] 存储
stringBuffer:可变的字符序列,线程安全的,效率低,底层使用char[] 存储
stringBuilder(jdk5.0新增的):可变的字符序列,线程不安全,效率高,底层使用char[] 存储
string->stringBuffer,stringBuilder:调用stringbuffer,stringBuilder的构造器
stringbuffer,stringBuilder->string:①调用string构造器;②stringBuffer,stringBuilder的toString()
3.JDK8之前日期API
1)system类中的currentTimeMills()返回当前时间与1970.1.1.0.0.0之间以毫秒为单位的时间差,称为时间戳。
2)Date类(java.util.Date/java.sql.Date)
①toString():显示当前的年,月,日,时,分,秒
②getTime():获取当前时间的时间戳(毫秒数)
构造器一:Date()创建一个对应当前时间的对象
构造器二:Date(long date)创建指定毫秒数的Date对象
③将一个java.util.Date转化为java.sql.date
情况一:强制类型转换 情况二
3)java.text.SimpleDateFormat类
使用:对日期类Date的格式化和解析
①格式化:日期—>字符串
②解析(格式化的逆过程):字符串—>日期
4)calendar类:抽象类
①实例化
方式一:创建子类(GregorianCalen)的对象
方式二:调用静态方法getInstance()
②常用方法
get() int days = calendar.get(calendar.DAY_OF_MONTH)获取当前日期在当前月份的第几天
set()calendar.set(calenda.DAY_OF_MONTH,22)
add()calendar.add(calendar,DAY_OG_MONTH,3)
getTime()日历类—>date类
setTime()date—>日历类
5)JDK8新增LocalDate LocalTime
①localDate.now(),LocalDate.now(),LocalTime.now():获取当前日期,时间,日期+时间
②LocalDateTime.of(year,month,day,hour,minute,second):设置指定的年月日时分秒,没有偏移量
③LocalDateTime.getXxx():获取相关属性,当前是时间所属月或年等的第几天
④LocalDate.withDayOfMonth(int i):设置相关属性,设置当前时间为当前月份的第几天。
⑤LocalDateTime.plusXxx(3),当前时间加几天,几月,几秒等,有返回值
6)瞬时Instant(类似于java.util.Date)类
①Instant instant = Instant.now();获取瞬时时间(本初子午线时间)
②offsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofhour(8))添加偏移量
③instant.toEpochMilli():获取自1970.1.1.0.0.0(UTC)开始的毫秒数
④ofEpochMilli():添加偏移量
7)DateTimeFormatter:格式化或解析日期,时间,类似于SimplDateFormate
4 Java比较器
Java中的对象正常情况下,只能进行比较:== 或!= 不能使用>或<
1)Comparable接口的使用
①像String,包装类等实现了Compareable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方法
②重写compareTo(obj)方法的规则:如果当前对象this大于形参对象obj,则返回正整数,小于形参对象obj返回负数,相等返回0
③对于自定义类,如果需要排序,我们可以让自定义类实现comparable接口,重写compareTo()方法
2)comparator定制排序
当元素的类型没有实现java.lang.Comparable接口又不方便修改代码,或者排序规则不适合当前操作则使用此方法
5 System类
6.Math类
7.BigInteger和Bigecimal
三 枚举类
1 说明
1)类的对象必须是有限个,确定的。
2)当需要定义一组常量时,强烈建议使用枚举类
3)如果枚举类只有一个对象,则可以作为单例模式的实现方式
2 定义
方式一:jdk5.0之前,自定义枚举类
方式二:jdk5.0之后可以使用enum关键字定义
方式一:
//1.声明season对象属性
private final String seansonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Seanson(String seansonName,String seasonDesc){
this.seansonName = seansonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象
public static final Seanson SPRING = new Seanson("春天","春暖花开");
public static final Seanson SUMMER = new Seanson("夏天","夏日炎炎");
public static final Seanson AUTUMN = new Seanson("秋天","秋高气爽");
public static final Seanson WINTER = new Seanson("冬天","冬雪皑皑");
//4.其他诉求,获取枚举类对象的属性
public String getSeansonName() {
return seansonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
方式二:使用enum定义枚举类
enum Seanson1 {
//1.提供当前枚举类的对象,多个对象之间用“,”隔开
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "冬雪皑皑");
// 2.声明season对象属性,private final修饰
private final String seansonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Seanson1(String seansonName, String seasonDesc) {
this.seansonName = seansonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象
//4.其他诉求,获取枚举类对象的属性
public String getSeansonName() {
return seansonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Seanson{" +
"seansonName='" + seansonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
3 enum常用方法
1)values():返回枚举类型的数组,该方法可以很方便的遍历所有的枚举值
2)values(string ObjName):返回枚举类中对象名时objname的对象,可以把一个字符串转为对应的枚举类对象,要求字符串必须是枚举类对象
3)toString():
4.使用enum定义枚举类实现接口
enum Seanson1 implements Info{
//1.提供当前枚举类的对象,多个对象之间用“,”隔开
SPRING("春天", "春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里");
}
},
SUMMER("夏天", "夏日炎炎"){
@Override
public void show() {
System.out.println("宁夏");
}
},
AUTUMN("秋天", "秋高气爽"){
@Override
public void show() {
System.out.println("秋天不回来");
}
},
WINTER("冬天", "冬雪皑皑"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
// 2.声明season对象属性,private final修饰
private final String seansonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Seanson1(String seansonName, String seasonDesc) {
this.seansonName = seansonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象
//4.其他诉求,获取枚举类对象的属性
public String getSeansonName() {
return seansonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Seanson{" +
"seansonName='" + seansonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
@Override
public void show() {
System.out.println("这是一个季节");
}
六、注解
一定程度上可以说:框架 = 注解 + 反射 + 设计模式
1)理解:
①JDK5之后新增的
2)示例
①生成相关文档的注解
②在编译时进行格式检查常用的有如下三个
@Override:限制重写父类方法
@Deprecated:用于表示所修饰的元素已过时,
@SuppressWarnings:控制编译器警告
3)自定义注解(参照SupressWarning)
①注解声明为:@interface
②内部定义成员,通常使用value表示
③可以指定成员的默认值,使用default定义
④如果自定义注解没有成员,表明是一个标识作用
4)元注解
①@Rerention指定所修饰的Annotation的生命周期:SOURCE\CLASS(默认行为)\RUNTIME,只有声明为RUNTIME的生命周期的注释,
Target:用于指定被修饰的Annotation能用于修饰哪些程序元素
@Decumented:用于指定
@Inherited:修饰的Annotation将具有继承性
5)通过反射获取注解信息
6)JDK8中注解的新特性:可重复注解,类型注解
可重复注解:在MyAnnotation上声明@Repeatable,成员值为MyAnnotation.class;MyAnnotation的Target和Retention和Annotation相同类注解
七、集合
集合和数组都是对多个数据进行存储操作的,简称Java容器,说明:此时的存储主要指的是内存方面的存储,不涉及到持久化的存储(txt,jpg,avi)
1.数组在存储多个数据方面的特点:
1)一旦初始化以后,长度确定。
2)定义数组时要声明数组类型,一旦定义好,其元素的类型就确定了,我们只能操作指定类型的数据。
3)一旦初始化,其长度就不可以修改。
4)数组中提供的方法非常有限,对于添加,删除,插入数据等操作,非常不便
5)获取数组中的实际元素的个数,数组没有现成的属性或方法可用。
6)数组存储数据的特点是有序的,可重复,对于无序,不可重复的需求不可满足。
2.Java集合可以分为Collection和Map两种体系
1)collection接口:单列集合 用来存储一个一个的对象
子接口①list: 存储有序的,可重复的数据 ---->“动态数组”
实现类:ArrayList、LinkedList、Vector
②set接口:存储无序的,不可重复的集合 ---->“集合”
实现类:HashSet、LinkedHashSet、TreeSet
2)map接口:双列集合,用来存储一对一对(key-value)的数据 ->“函数”
实现类:HashMap、LinkHashMap、TreeMap、Hashtable、Properties
集合collection中春初的如果是自定义类的对象,需要自定义重写equals方法。
3.collection接口中的Api,方法
1)1-4
public class CollectionTest {
@Test
public void test1(){
//add(object e)将元素e添加到集合coll中
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add("123");
coll.add(new Date());
// coll.add("AA");
//size():获取添加元素的个数
System.out.println(coll.size());
//addAll(Collection coll1):将coll1集合中的元素添加到当前集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("CC");
coll.addAll(coll1);
System.out.println(coll.size());
System.out.println(coll);
//clear():清空集合元素
coll.clear();
//isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty());
}
2)5-13
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
//1.contains(Object obj):判断当前集合是否包含obj
//我们在判断时会调用obj对象所在类的equals()
boolean contains = coll.contains(123);
System.out.println(contains);
System.out.println(coll.contains(new String("Tom")));
System.out.println(coll.contains(p));
//2.contains All(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。
Collection coll1 = Arrays.asList(123,456);
System.out.println(coll.contains(coll1));
}
@Test
public void test2(){
//3remove(Object obj):从当前集合中一处obj元素
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
coll.remove(123);
System.out.println(coll);
coll.remove(new Person("Jerry",20));
//4.removeAll(Collection coll1):从当前集合中移除coll1中所有的元素
Collection coll1 = Arrays.asList(123,4567);
coll.removeAll(coll1);
System.out.println(coll);
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
//5.retainAll(Collection coll1):交集,获取当前集合和coll1集合的交集,并返回给当前集合
//6.equals(Object obj):要想返回True,需要当前集合和形参的内容相同
Collection coll1 = new ArrayList();
coll1.add(123);
coll1.add(456);
coll1.add(new String("Tom"));
coll1.add(false);
Person p1 = new Person("Jerry",20);
coll1.add(p);
System.out.println(coll.equals(coll1));
}
@Test
public void test4(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
//7.hahCode():返回当前对象的哈希值
System.out.println(coll.hashCode());
//8.集合->数组:toArray()
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//拓展:数组->集合:调用Arrays类的静态方法aList()
List<String> list = Arrays.asList(new String[]{"AA","BB","CC"});
//iterctor():返回Tterator接口的实例,用于遍历集合元素
}
3)14iterator,iterctor():返回Tterator接口的实例,用于遍历集合元素
内部方法next()和hasNext()
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
Iterator iterator = coll.iterator();
//方式一
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
//方式二
for (int i = 0; i < coll.size(); i++) {
System.out.println(iterator.next());
}
//方式三:推荐使用
//hashNext():判断是否还有下一个元素
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
Person p = new Person("Jerry",20);
coll.add(p);
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
Iterator iterator1 = coll.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}
}
4 增强for循环(foreach)
jdk5之后新增加,用于遍历集合数组 ,内部仍然调用迭代器
集合遍历格式:for(集合对象 局部变量 :集合对象){}
5 List接口概述
list接口的方法
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",20));
list.add(456);
//int indexOf(Object obj):返回obj在集合中首次出现的位置,如果不存在,返回-1
int index = list.indexOf(456);
System.out.println(index);
//int LastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf(456));
//Object remove(int index):移除指定位置的元素,并返回此元素
Object remove = list.remove(0);
System.out.println(remove);
System.out.println(list);
//Object set(int index,Object ele):设置指定位置元素额为ele
list.set(1,"AA");
System.out.println(list);
//List subList(int fromIndex,int toIndex):返回重fromIndex到toIndex位置的子集合
List list1 = list.subList(2, 4);
System.out.println(list1);
}
@Test
public void test1() {
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",20));
list.add(456);
System.out.println(list);
//void (int index,Object ele):在index位置插入ele元素
list.add(1,"BB");
System.out.println(list);
//boolean add(int index,Collection eles):从index位置开始将eles中的所有元素添加到
List list1 = Arrays.asList(1,2,3);
list.addAll(list1);
System.out.println(list.size());
//Object get(int index):获取指定index位置的元素
Object obj = list.get(3);
System.out.println(obj);
}
总结:常用方法
1)增 add(Object obj)
2)删 remove(int index)/remove(Object obj)
3)改 set(int index,Object ele)
4)查 get(int index)
5)插 add(int index,Object ele)
6)长度 size()
7)遍历 ①Interator迭代器②增强for循环③普通的循环
6 set接口
1)具体实现类:
HashSet:作为et接口的主要实现类,线程不安全,可以储存null值,
LinkedHashSet:作为HashSet的子类出现的,遍历器内部数据时,可以按照添加 的顺序遍历,
TreeSet:可以按照添加的对象指定属性进行排序
set接口中没有额外定义新的方法,使用的都是collection中生命过的方法。
2)HashSet
①无序性:不等于随机性 ,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的
②不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个。我们向hashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组的存放位置(即为:索引位置),判断数组位置上是否已有元素,添加成功,元素a与已存在指定索引位置上数据,以链表的方式存储。
要求:向set中添加的数据,其所在的类一定要重写hashCode()和equals(),重写的hashcode()和equals()尽可能保持一致,相等的对象必须具有相等的散列码,重写两个方法的小技巧:对象中用作equals()方法比较的Field都应该用来计算hashCode。
3)LinkedSet
作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。对于比较频繁的遍历操作,效率高于HashSet
3)TreeSet
①向TreeSet中添加的数据,要求是相同类的对象。
②两种排序方式:自然排序和定制排序
③在自然排序中,比较两个对象是否相同的标准:compareTo()返回为0,不再是equals。
在定制排序中,比较两个对象是否相同的标准:compare()返回为0,不再是equals。
7 map接口
1)双列数据,存储key-value对的数据,类似于高中的函数,Map中的key:无序不可重复,使用Set存储所有的key,key所在的类要重写equals()和hashCode();Map中的value:无序的可重复的,使用collection存储所有的value,value所在的类要重写equals方法;一个键值对:key-value构成了一个Entry对象,Map中的Entry:无序的,不可重复的,使用Set存储所有的Entry
HashMap:底层:数组+链表(jdk7之前),数组+链表+红黑树(jdk8);作为Map的主要实现类,线程不安全,效率高,可以存null的key和value;LinkedHashMap是HashMap的子类,保证在遍历Map元素时,可以按照添加的顺序实现遍历
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序,底层使用红黑树
Hashtable:Map的古老实现类,线程安全,效率低 Propertie常用来处理配置文件,key和value都是String类型
2)在不断地添加过程中,会涉及到扩容问题,默认扩容方式为:扩容为原来的两倍,并将原有数据复制过来
jdk8相较于jdk7底层实现方面的不同:new HaspMap():底层没有创建一个长度为16的数组;底层数组为Node[] ,而非Entry[];首次调用put()方法时,底层创建长度为16的数组
3)Map中定义的方法
@Test
public void test3(){
Map map = new HashMap();
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//遍历所有的key集
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// 遍历所有的value集
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);
}
//遍历所有的key-value
//方式一
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "----->" + entry.getValue());
}
//方式二
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "----->" + value);
}
}
@Test
public void test2(){
Map map = new HashMap();
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//Object get(Object key)
map.get(45);
//containsKey(Object key)
boolean isExit = map.containsKey("BB");
System.out.println(isExit);
map.clear();
System.out.println(map.isEmpty());
}
@Test
public void test1(){
Map map = new HashMap();
//添加
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//修改
map.put("AA",87);
// map.put("AA",123);
System.out.println(map);
Map map1 = new HashMap();
map.put("CC",123);
map.put("DD",123);
map.putAll(map1);
System.out.println(map);
// remove(Object key)
Object value = map.remove("CC");
System.out.println(value);
System.out.println(map);
// clear();
map.clear();//与map==null不同
map.size();
System.out.println(map.size());
System.out.println(map);
}
8 Collections工具类
1)常用方法
八、泛型
1.概念和使用
类似于标签,把元素的类型设置为一个参数
类型推断:实例化时等号右边的泛型可省略
2 自定义泛型类
泛型类,泛型接口,泛型方法
由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象,不需要再指明泛型。静态方法中不能使用类的泛型,异常类不能使用类的泛型
泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系,换句话说,泛型方法所属的类是不是泛型都没有关系。泛型方法可以声明为静态的,泛型参数是在调用方法的时候确定的,并非在实例化的时候确定
泛型在继承方面的体现:类A时类B的父类,G< A > 和G< B >二者不具备父子类关系,二者是并列关系
3 通配符的使用:(?)
类A和类B,G< A >和G< B >是没有关系的,二者共同的父类是:G<?> 对于list<?>就不能往其内部添加数据,除了添加null之外,允许读取数据,读取的数据类型为object
有限制条件的通配符
1) ? extends A; G<? extends A>可以作为G< A >和G< B >的父类的,其中B是A的子类
- ? super A :G<? super A> 可以作为G< A > 和G< B >的父类,其中B是A的
九、IO流
1.相关概念
1)File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
2).File声明在java.io包下
3)File类中设计到关于文件或文件目录的创建,重命名,修改时间,文件大小等方法,并未涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,必须使用IO流
4)后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的终点,
5)如何创建File类的实例
File(String filePath)
File(String parentPath,String childPath)
File(File parentFile,String childPath)
相对路径与绝对路径
路径分隔符
2 相关方法
1)
@Test
public void test1(){
//构造器1
File file1 = new File("hello.txt");//相当于当前module
File file2 = new File("D:\\beiyong");
System.out.println(file1);
System.out.println(file2);
//构造器2
File file3 = new File("D\\beiyong","JavaSenior");
File file4 = new File(file3, "hi.txt");
}
@Test
public void test2(){
File file1 = new File("hello.txt");
File file2 = new File("D:\\IO\\hi.txt");
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getPath());
System.out.println(file1.getName());
System.out.println(file1.getParent());
System.out.println(file1.length());
System.out.println(new Date(file1.lastModified()));
System.out.println("************************************");
System.out.println(file2.getAbsolutePath());
System.out.println(file2.getPath());
System.out.println(file2.getName());
System.out.println(file2.getParent());
System.out.println(file2.length());
System.out.println(file2.lastModified());
}
@Test
public void test3(){
File file1 = new File("D:\\workspace\\workspace_idea1\\JavaSenior");
String[] list = file1.list();
for(String s : list){
System.out.println(s);
}
File[] files = file1.listFiles();
for(File f : files){
System.out.println(f);
}
}
@Test
public void test4(){
File file1 = new File("hello.txt");
File file2 = new File("D:\\IO\\hi.txt");
boolean b = file1.renameTo(file2);//要想返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在
System.out.println(b);
}
@Test
public void test5(){
File file1 = new File("hello.txt");
System.out.println(file1.isDirectory());
System.out.println(file1.isFile());
System.out.println(file1.exists());
System.out.println(file1.canRead());
System.out.println(file1.canWrite());
System.out.println(file1.isHidden());
File file2 = new File("D\\IO");
}
//创建硬盘中对应的文件或文件目录
@Test
public void test6() throws IOException {
//文件的创建
File file1 = new File("hi.txt");
if(!file1.exists()){
file1.createNewFile();
System.out.println("创建成功");
}else{
file1.delete();
System.out.println("删除成功");
}
}
@Test
public void test7(){
//文件目录的创建
File file = new File("D:\\IO\\io1");
boolean mkdir = file.mkdir();
if (mkdir) {
System.out.println("创建成功");
}
File file1 = new File("D:\\IO\\io2");
boolean mkdir1 = file1.mkdir();
if (mkdir1) {
System.out.println("创建成功2");
}
File file2 = new File("D:\\IO\\io1\\io3");
boolean mkdir2 = file2.mkdirs();
if (mkdir2) {
System.out.println("创建成功2");
}
}
}
3 流的分类
1)按照操作数据单位不同分为:字节流(8bit),字符流(16bit)
2)按照数据流向不同分为:输入流,输出流
3)按照流的角色不同:节点流,处理流
4 流的操作
1)read()的理解:返回读入的一个字符,如果达到文件末尾返回-1,
2)异常的处理,为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理
3)读入的文件一定要存在,否则就会报FileNotFoundException
4)输出操作,硬盘对应的File可以不存在,并不会报异常
①如果不存在:在输出的过程中,会自动的创建此文件,
②如果存在:如果流使用的构造器是:FileWriter(file,false)/FileWriter(file):对原有文件的进行覆盖
如果流使用的构造器是:FileWriter(file,true):对原有文件不覆盖,进行追加
5)字符流相关操作
//将day09下的hello.txt文件内容读入程序中,并输出到控制台
@Test
public void test1(){
FileReader fr = null;
try {
//1.实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");
//2.提供具体的流
fr = new FileReader(file);
//3.数据的读入过程
//read()返回读入的一个字符,如果达到文件末尾,返回-1
//方式一
// int data = fr.read();
// while(data != -1){
// System.out.print((char) data);
// data = fr.read();
// }
//方式二
int data;
while((data = fr.read()) != -1){
System.out.println((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的关闭操作
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//对read()操作的升级,使用read的重载方法
@Test
public void testFileReader1() {
FileReader fr = null;
//1.File类的实例化
try {
File file = new File("hello.txt");
//FileReader流的实例化
fr = new FileReader(file);
//3.读入的操作
//read()返回每次读入cbuf数组中的字符的个数,如果达到文件末尾,返回-1
char[] cbuf = new char[5];
int len;
while((len = fr.read(cbuf)) != -1){
//错误写法
// for(int i = 0;i < cbuf.length;i++){
// System.out.print(cbuf[i]);
// }
for(int i = 0; i < len;i++){
System.out.print(cbuf[i]);
}
}
;
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源的关闭
try {
if(fr != null){
fr.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 从内存中写出数据到硬盘的文件
@Test
public void testFileWriter() {
FileWriter fw = null;
try {
//1.提供File类的对象,指明写出到的文件
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
fw = new FileWriter(file,true);
//3.写出的操作
fw.write("I have a dream\n".toCharArray());
fw.write("you need to have a dream,too".toCharArray());
} catch (IOException e) {
e.printStackTrace();
}finally {
//4.流资源的关闭
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testFileReaderFileWriter(){
//1.创建File类的对象,指明都如何写出的文件
FileReader fr = null;
FileWriter fw = null;
try {
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符个数
while ((len = fr.read(cbuf)) != -1){
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6)不能用字符流的方式处理图片等字节数据,对于文本文件(.txt,.java,.c.cpp),使用字符流处理,对于非文本文件(.jpg,.mp3,.mp4,.avi.doc,.ppt…),使用字节流处理。
字节流处理
@Test
public void testFileInputStream() {
FileInputStream fis = null;
try {
//1.造文件
File file = new File("hello.txt");
//2.造流
fis = new FileInputStream(file);
//读数据
byte[] buffer = new byte[5];
int len;
while ((len = fis.read(buffer)) != -1){
String str = new String(buffer,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//实现对图片的复制
@Test
public void testFileInputOutputStream(){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File srcFile = new File("LoveandFriend.jpg");
File destFile = new File("LoveandFriend1.jpg");
//
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//复制的过程
byte[] buffer = new byte[5];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//指定路径下文件的复制操作
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//复制的过程
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
拓展:文件复制(字节流)
//指定路径下文件的复制操作
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//复制的过程
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7)缓冲流的使用
作用:提供流的读取、写入的速度 机制:内部提供了一个缓冲区。
8)转换流:
①属于字符流,InputStreamReader:将一个字节的输入流转换为字符的输入流,OutPutStream:将一个字符的输出流转换为字节的输出流
②作用:提供字节流与字符流之间的转换
③解码:字符、字节数组–> 字符数组、字符串
编码:字符数组、字符串–> 字节、字节数组
5 其他流
1)标准的输入输出流
①System.in 标准的输入流,默认从键盘获取
②System.out:标准的输出流,默认从控制台输出
③system类的setIn(InputStream is)/setOut(OutPutStream os)方式重新指定输入和输出的流
2)数据流
①DataInputStream和DataOutputStream
作用:用于读取或写出基本数据类型变量或字符串
读取不同类型的数据的顺序要与当初写入文件时,保存数据的顺序一致。
6 对象流
1)ObjectInputStream和ObjectOutputStream
2)作用:用于存储和读取基本数据类型数据或对象的处理流,他的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
3)Person需要满足以下的要求方可序列化
① 需要实现接口:Serializable
②当前类需要提供一个全局常量
③除了当前Person类需要实现Serializable接口之外,还必须保证其内部属性也必须是可序列化的,默认情况下,基本数据类型也是可序列化的
④补充:ObjectInputStream和ObjectOutputStream不能序列化static和transient修饰的成员变量
7 RandomAccessFile(随机存取文件流)
既可以作为输入流也可以作为输出流
①直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
② RandomAccessFile作为一个输出流出现时,写出的文件如果不存在,则在执行过程中自动创建,如果写出文件存在,则会对原有文件进行覆盖(默认情况下从头覆盖
十、网络编程
1.两个要素
1)网络编程两个要素:①IP和端口号;②提供网络通信协议
2)通信要素:
①IP和端口号
1.在Java中使用InetAddress类代表IP
2 常用方法:getHostName()和getHostAdderss()
3.端口号与ip地址组合得出一个网络套接字socket
②网络通信协议
3)TCP网络编程
①客户端发送信息给服务端,服务端将数据显示在控制台上
public class TCPTest1 {
//客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 8899);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据
os.write("你好,我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 资源的关闭
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//服务端
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建服务器端的socket,指明自己的端口号
ss = new ServerSocket(8899);
//2.调用accept()表示接受来自客户端的socket
socket = ss.accept();
//3.获取输入流
is = socket.getInputStream();
//不建议这种方式,可能会有乱码
// byte[] buffer = new byte[20];
// int len;
// while ((len = is.read(buffer)) != -1){
// String str = new String(buffer,0,len);
// System.out.print(str);
// }
//4.读取输入流的数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[20];
int len;
while ((len = is.read(buffer)) != -1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
//5.资源的关闭
if(baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
②客户端发送文件给服务端,服务端将文件保存在本地
@Test
public void client() throws IOException {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("LoveandFriend.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
fis.close();
os.close();
socket.close();
}
@Test
public void server() throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream("beautify.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
fos.close();
is.close();
socket.close();
ss.close();
}
③客户端发送文件给服务端,服务端将文件保存在本地,并返回“发送成功”给客户端。
package com.atguigu.java1;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author lyz
* @create 2020-06-17 15:00
*/
public class TCPTest3 {
@Test
public void client() throws IOException {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("LoveandFriend.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
socket.shutdownOutput();
//接受来自服务器端的信息,并展示在控制台上
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer1 = new byte[1024];
int len1;
while((len1 = is.read(buffer1)) != -1){
baos.write(buffer1,0,len1);
}
System.out.println(baos.toString());
baos.close();
fis.close();
os.close();
socket.close();
}
@Test
public void server() throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream("beautify2.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
//服务器端给客户端反馈
OutputStream os = socket.getOutputStream();
os.write("你好,照片已经收到,很漂亮".getBytes());
os.close();
fos.close();
is.close();
socket.close();
ss.close();
}
}
4)UDP编程
十一、反射
1.概念
1)作用
2)主要API
3)主要实现
@Test
public void test1(){
Person p1 = new Person("Tom",12);
p1.age = 10;
System.out.println(p1);
p1.show();
}
@Test
public void test2() throws Exception{
//1.通过反射可以创建Person类的对象
Class clazz = Person.class;
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person)obj;
System.out.println(obj.toString());
//2.通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//通过反射,可以调用Person类的私有的结构
//调用私有的构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
//调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"HanMeimei");
System.out.println(p1);
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1, "中国");
System.out.println(nation);
}
2 理解
1)关于java.lang.Class类的理解
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例
2.换句话说,Class的实例就对应者一个运行时类
3.加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类
2)获取Class的实例的方式
@Test
public void test3() throws ClassNotFoundException {
//方法一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.atguigu.java.Person");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
}
3 创建运行时类
通过反射创建对应的运行时类的对象
获取运行时类的方法结构
public class MethodTest {
@Test
public void test1(){
Class clazz = Person.class;
//getMethods():获取当前运行时类及其父类中声明为public权限的方法
Method[] method = clazz.getMethods();
for(Method m : method){
System.out.println(m);
}
System.out.println();
//getDeclareMethods()获取当前运行时类的所有方法(不包括父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
System.out.println(m);
}
}
/*
@Xxx
权限修饰符 返回值类型 方法名(参数类型1,形参名1,........) throws XxxException{}
*/
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
//1.获取方法声明的注释
Annotation[] annotations = m.getAnnotations();
for(Annotation a : annotations){
System.out.println(a);
}
//2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//3.返回值类型
System.out.print(m.getReturnType().getName() + "\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形参列表
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for(int i = 0; i < parameterTypes.length;i++){
if(i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i);
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(!(exceptionTypes == null && exceptionTypes.length == 0)){
System.out.print("throws");
for (int i = 0; i < exceptionTypes.length; i++) {
if(i == exceptionTypes.length - 1){
System.out.println(exceptionTypes[i].getName());
break;
}
System.out.println(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
}
获取当前运行时类的属性结构
public class FieldTest {
@Test
public void test1() {
Class clazz = Person.class;
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}
}
@Test
public void test2() {
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
//1.权限修饰符
int modifiers = f.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");
//2.数据类型
Class<?> type = f.getType();
System.out.print(type.getName() + "\t");
//变量名
String name = f.getName();
System.out.println(name);
}
}
}
4 运行时类相关操作
1)调用运行时类中指定的结构:属性,方法,构造器。
调用运行时类的指定结构和获取运行时类的结构是不一样的。通过前面的知识可以得知类中的非静态结构是需要类的对象去调用,获取运行时类的结构(诸如属性和方法)只需要拿到运行时类就可以通过这个类去获取,但是调用运行时类的非静态结构,只有通过对象才能调用和控制,所以想要调用运行时类的结构,首先要拿到这个运行时类,再创建这个运行时类的对象,通过这个对象来调用这个运行时类的结构。
2)获取运行时类的属性和方法时,需要注意,通过getField()和getMethod()直接获取的属性和方法只能获取权限修饰符为public,想要获取所有的属性和方法就要调用getDeclaredField()和getDeclaredMethod() ,这两个方法的方法名后面加s,是获取所有的属性或方法,不加s,在参数中写明要获取的方法名或属性名,可以获取指定的属性或方法。
3)对于通过getField()和getMethod()直接获取的属性和方法,可以直接通过set(),get()和invoke()进行操作,而对于getDeclaredField()和getDeclaredMethod(),由于存在非public修饰的属性或方法,操作这种方式获取到的属性或变量前,对其调用setAccessible(true),让这些属性或方法是可获取的,之后才能拿到这些属性和方法。
4)invoke(),在 获取到运行时类的方法之后,通过invoke()就可以调用该运行时类的方法,invoke()的参数是可变形参,第一个参数是运行时类的对象,后面的参数是调用运行时类方法想要传入的参数。invoke方法的返回值就是所调用的方法的返回值,如果调用的方法没有返回值,返回NULL。
@Test
public void testField() throws Exception {
Class clazz = Person.class;
//创建运行时类对象
Person p = (Person) clazz.newInstance();
//1.获取指定的属性:要求运行时类中属性声明为public(通常不采用此方法)
Field id = clazz.getField("id");
//2.设置当前属性的值:set():参数1:指明设置那个对象的属性 参数2:将此属性值设置为多少
id.set(p,1001);
//3.获取当前属性的值:get():参数1:获取那个对象的当前属性值
int pId = (int)id.get(p);
}
//如何操作运行时类中指定的属性(需要掌握)
@Test
public void testField1() throws Exception {
Class clazz = Person.class;
//创建运行时类对象
Person p = (Person) clazz.newInstance();
//1.getDeclareField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可以访问的
name.setAccessible(true);
//3.可以获取设置指定对象的此属性值
name.set(p,"Tom");
System.out.println(name.get(p));
}
@Test
public void testMethod() throws Exception {
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
//1.获取运行时类对象指定方法
// getDeclareMethod(): 参数1:指明获取的方法的名称,参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show",String.class);
show.setAccessible(true);
//invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()的返回值几位对应类中调用的方法的返回值
Object chn = show.invoke(p, "CHN");
System.out.println(chn);
System.out.println("**********如何调用静态方法***********");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);
}
//调用运行时类中指定的构造器
@Test
public void testConstractor() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Person.class;
//1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保障此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
}
5 总结
1)获取Class实例的三种常用方法
Class clazz = String.class;
Class clazz = person.getClass();
Class clazz = Class.Forname(String classPath);//体现反射的动态性
2)创建Class对应运行时类的对象的通用方法,
Object obj = clazz.newInstance();//创建了对应的运行时类的对象
①必须有空参的构造器 ②权限修饰符的权限,通常设置为public
这种方式通用于框架底层,相对于其他方式更常用。
6 应用:动态代理
1)静态代理
特点:代理类和被代理类在编译期间就确定下来了
interface ClothFactory{
void productCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//就用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void productCloth() {
System.out.println("代理工厂做一些准备");
factory.productCloth();
System.out.println("代理工厂做一些后续的收尾工作");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
public class StaticProxyTest{
public static void main(String[] args) {
NikeClothFactory nike = new NikeClothFactory();
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.productCloth();
}
}
2)动态代理
在程序运行时,为被代理类动态的创建代理类进行代理。
动态代理区别于静态代理在于,动态代理在代码编译期并不能确实要代理的是哪个类,所以不能像静态代理一样直接创建代理类。代理模式的实现(代理类和被代理类产生联系)主要是通过实现同一个接口,从反射中可以得知,我们可以通过反射动态的获取运行时类的结构(包括接口),由此,我们便可以创建实现了被代理类实现的接口的类,从而实现代理。
package com.atguigu.com;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author lyz
* @create 2020-06-19 16:40
*/
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe i can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class ProxyFactory{
//调用此方法返回一个代理类的对象
public static Object GetProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler hander = new MyInvocationHandler();
hander.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hander);
}
public static void GetProxyInstance() {
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//赋值时需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法a的功能就生命在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:几位代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnVal = method.invoke(obj, args);
//上述方法的返回值就作为当前类中的invoke()中的返回值
return returnVal;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human proxyInstance = (Human) ProxyFactory.GetProxyInstance(superMan);
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("四川麻辣烫");
}
}