19.final的使用:
作用:
1)final修饰类,该类不能被继承
2)final修饰方法,该方法不能被子类重写
3)final修饰变量,变量变为常量,不能重写数据
注意:
1)常量在常量池中声明,项目结束时才会被销毁
2)常量的命名规范,全部单词大写,单词之间使用下划线隔开
提示:
1)如果不想让该类有子类,就可以用final修饰
2)final和static一般修饰属性,该属性变成静态属性
19.抽象类及抽象方法:
概念:没有代码块,使用abstract修饰的方法,交给非抽象子类去实现
需求:编写人类为父类,编写两个子类(中国人、日本人)
public abstract class Person {
private String name;
private String sex;
private int age;
public Person() {
}
public Person(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/*
注意:abstract方法必须要在abstract类中
*/
public abstract void eat();
public void sleep(){
System.out.println(this.name+"睡觉觉");
}
}
package day05;
public abstract class Chinese extends Person{
private String id;
public Chinese() {
}
public Chinese(String name, String sex, int age, String id) {
super(name, sex, age);
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public abstract void hobbies();
}
public class SiChuan extends Chinese{
public SiChuan() {
}
public SiChuan(String name, String sex, int age, String id) {
super(name, sex, age, id);
}
@Override
public void hobbies() {
System.out.println(super.getName()+"打麻将");
}
@Override
public void eat() {
System.out.println(super.getName()+"吃火锅");
}
}
public class Japanese extends Person{
private String yearNum;
public Japanese() {
}
public Japanese(String name, String sex, int age, String yearNum) {
super(name, sex, age);
this.yearNum = yearNum;
}
@Override
public void eat() {
System.out.println(super.getName()+"吃海鲜");
}
}
package day05;
public class test_01 {
public static void main(String[] args) {
Japanese j = new Japanese("x","女",18,"xxx");
j.eat();
j.sleep();
System.out.println("-------------------------------");
SiChuan siChuan = new SiChuan("xxxx","nan",12,"2341341");
siChuan.eat();
siChuan.sleep();
}
}
面试题:
1.抽象类不能有构造方法?
抽象类可以有构造方法
2.抽象类中只能有抽象方法?
抽象类中可以有属性、构造方法、成员方法、静态方法、抽象方法等等。
3.抽象类中不可以没有抽象方法?
可以,但是这样毫无意义(因为某些原因讲方法设计成抽象的,抽象方法必须在抽象类中,这时你才会使用到抽象类)
4.什么原因讲方法设计成抽象的?
这个方法应该在该类中,但是该方法不太好实现,就将方法设计成抽象的,交给非抽象的子类去实现。
5.如果父类是抽象类,则子类必须实现父类的抽象方法?
不一定,如果子类是抽象类,可以不实现父类的抽象方法。
6.可以使用new关键字来创建抽象类对象?
不可以
20.接口
理解:
1.接口是一个特殊的抽象类
2.JDK1.8之前,就扣中只能有抽象方法及静态常量
3.JDK1.8开始,接口中允许使用抽象方法、静态常量、静态方法、默认方法
注意:
1.接口中的抽象方法默认添加public abstract修饰(一般把abstract去掉)
应用场景:接口相当于是制定规则、并让实现类去实现。
面试题:
1.一个类可以实现多个接口吗?
可以
2.一个接口可以实现多个接口吗?
不可以,但是一个接口可以继承多个接口
3.接口里面的方法不一定是抽象的?
是的,
JDK1.8之前,就扣中只能有抽象方法及静态常量
JDK1.8开始,接口中允许使用抽象方法、静态常量、静态方法、默认方法
4.接口解决了类的单继承问题?
是的
5.一个类是否可以继承一个类并同时实现多个接口?
是的,因为一个类可以实现多个接口
6.接口可以new对象?
不可以
类 - 接口的关系:
类 - 类:单继承(一个类只能继承另一个类,不能继承多个类)
类 - 接口:多实现(一个类可以实现多个接口)
接口 - 接口:多继承(一个接口可以继承多个接口)
21.多态
概念:同⼀个⾏为具有不同表现形式的能⼒。
java中有两种方式可以实现多态:
1)继承父类
2)实现一个接口。
22.对象转型
自动转型:向上转型:子类类型转父类类型
强制转型:向下转型:父类类型转子类类型
注意:
向上转型:
1)向上转型就是多态 Father father = new Son();
2)向上转型后,可以调用父类属性的方法
3)向上转型后,不可以调用子类独有的属性和方法
向下转型:
1)向下转型是不安全的 --会出现ClassException类转换异常
2)出现ClassException,一定要看错误信息。
3)父类对象不能赋值给子类引用 – Dog dog = (Dog) new Animal();
4)向下转型之前必须先向上转型
Animal an = new Dog();
Dog dog = (Dog) an;
5)向下转型之前,使用instanceof判断类型。
23.内部类
理解:一个类中,再声明另外一个类
分类:
1)成员内部类
2)静态内部类
3)接口内部类
4)局部内部类
5)匿名内部类
//外部类
public class Outer {
//成员内部类
class Inner01{}
//静态内部类
class static Inner02{}
public void method(){
//局部内部类
class Inner03{}
}
}
成员内部类:
小结:
1)创建成员内部类对象之前,必须创建外部类对象
2)成员内部类可以调用外部类的所有的属性
3)在成员内部类中调用指定的外部类属性:外部类.this.属性
//外部类
public class Outter {
//外部属性
private String str1 = "外部类属性1";
protected String str2 = "外部类属性2";
public String str3 = "外部类属性3";
final String str4 = "外部类属性4";
static String str5 = "外部类属性5";
static final String str6 = "外部类属性6";
String str7 = "外部类属性7";
//成员内部类
class Inner{
//成员内部类属性
String str1 = "成员内部类属性";
public void InnerMethod(){
System.out.println("成员内部类方法");
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
System.out.println(str4);
System.out.println(str5);
System.out.println(str6);
System.out.println(str7);
}
}
}
public class Outter_test01 {
public static void main(String[] args) {
//创建成员内部类的对象
Outter.Inner inner = new Outter().new Inner();
inner.InnerMethod();
}
}
静态内部类
小结:
1)创建静态内部类对象,不用创建外部类对象
2)静态内部类只能调用外部类静态的属性
//外部类
public class Outter_static {
static String str1="外部类属性1";
static final String str2="外部类属性2";
//内部类
static class Inner{
public void InnerMethod(){
System.out.println("静态内部类方法");
System.out.println(str1);
System.out.println(str2);
}
}
}
public class Outter_test02 {
public static void main(String[] args) {
Outter_static.Inner inner = new Outter_static.Inner();
inner.InnerMethod();
}
}
接口内部类
小结:
1)接口内部类的使用方式和静态内部类一致
//外部接口
public interface Outter_interface {
//接口内部类
//默认使用public static修饰
class Inner{
public void innerMethod(){
System.out.println("接口内部类的方法");
}
}
}
public class Outter_test03 {
public static void main(String[] args) {
//创建静态内部类的对象
Outter_interface.Inner inner = new Outter_interface.Inner();
//调用方法
inner.innerMethod();
}
}
局部内部类
小结:
1)局部内部类不能使用访问修饰符
2)局部内部类的作用域就在外部类方法中
//外部类
public class Outter_jubu {
public void method(){
//局部内部类
class Inner{
public void innerMethod(){
System.out.println("局部内部类的方法");
}
}
//创建局部内部类对象
Inner inner = new Inner();
//调用方法
inner.innerMethod();
}
}
public class Outter_test04 {
public static void main(String[] args) {
Outter_jubu outter_jubu = new Outter_jubu();
outter_jubu.method();
}
}
局部内部类面试题:
1.局部内部类使用到外部类的局部变量时,为什么局部变量会变为常量。
24.包装类
理解:8中基本数据类型对应的类叫做包装类
出现原因:
java为纯面向对象语言(万物皆对象),8种基本数据类型不能new对象,就破坏了Java为纯面向对象的类,这种类叫做包装类/封装类
基本数据类型 包装类 继承关系
byte Byte Object.Number.Byte
short Short Object.Number.Short
int Integer Object.Number.Integer
long Long Object.Number.Long
float Float Object.Number.Float
double Double Object.Number.Double
char Character Object.Number.Character
boolean Boolean Object.Number.Boolean
手动装箱:基本数据类型->包装类(转换)
int num = 100;
Integer integer = Integer.ValueOf(num);
System.out.println(integer);
**手动拆箱:**包装类 ->基本数据类型
Integer integer = new Integer(100);
int num = integer.intValue();
System.out.println(num);
**自动装箱:**基本数据类型 -> 包装类
int num = 100;
Integer integer = num; //底层实现:Integer.valueOf(num);
System.out.println(Integer);
自动拆箱:包装类 -> 基本数据类型
Integer integer = new Integer(100);
int num = integer; //底层实现:intteger.intValue();
System.out.println(num);
将字符串转换为int值:
String str = “123”;
int num = Integer.parseInt(str);
System.out.println(num);
面试题:描述下列代码的运行结果
Integer integer1 = Integer.valueOf(100);
Integer integer2 = Integer.valueOf(100);
System.out.println(integer1 == integer2); true
Integer integer3 = Integer.valueOf(200);
Integer integer4 = Integer.valueOf(200);
System.out.println(integer1 == integer2); //false
面试题:描述Integer的valueOf底层实现原理
答:判断输入的int值是否在-128~127的区间内,
如果在就从Integer的缓存类的缓存数组中获取对象
如果不在,就重新new Integer对象
25.字符串相关类
分类:
1.String:不可变字符串
2.StringBuffer
3.StringBuilder
public class StringMethod {
public static void main(String[] args) {
/*
知识点:String的使用
*/
String str = "123abc";
str = str.concat("def456");//拼接字符串,并返回新的字符串
str = str.substring(2);//从开始下标截取到末尾,并返回新的字符串
str = str.substring(1,7);//从开始下摆哦截取到结束下标(排他),并返回新的字符串
str = " 123 abc def 123 ";
str = str.trim();//去除首尾空格,并返回新的字符串
str = str.replace('a','A');//替换字符串,并返回新的字符串
str = str.replace("Abc","ABC");//替换字符串,并返回新的字符串
str = str.replace("23","66");//替换第一次出现的字符串,并返回新的字符串
str = str.replaceAll(" ","");//去除空格(将空格字符串替换成空内容的字符串)
System.out.println("获取字符串的字符个数:"+str.length());
System.out.println("判断两个字符串内容是否相等(区分大小写):"+str.equals("166ABCdef166"));
System.out.println("判断两个字符串是否相同(不区分大小写):"+str.equalsIgnoreCase("166ABCDEf166"));
System.out.println("判断子字符串在此字符串第一次出现的下标:"+str.indexOf("1"));
System.out.println("获取字符串中指定下标的字符:"+str.charAt(6));
System.out.println(str);
//把其他类型转换为String类型
System.out.println(String.valueOf(10));//int->String
System.out.println(String.valueOf(11.11));//double->String
System.out.println(String.valueOf(true));//boolean->String
System.out.println(String.valueOf('A'));//char->String
System.out.println("-------------------------");
//把其它类型转换为String类型
System.out.println(10 + "");//int->String
System.out.println(11.11+""); //double->String
System.out.println(true+"");//boolean->String
System.out.println('A'+"");//char->String
System.out.println("-------------------------");
}
}
import java.util.Scanner;
public class StringPractise {
public static void main(String[] args) {
/**
* String练习题
* 练习:完成一个邮箱的校验hhy@qq.com
* 1)"@"不能在第一位
* 2)”.”不能在最后一位
* 3)“@”和“.”中间应该有字符
* 4)***@***.***
*/
boolean flag = true;
while (flag){
System.out.println("请输入有效的邮箱:");
Scanner scanner = new Scanner(System.in);
String email = scanner.next();
if (email.indexOf("@") == 0){
System.out.println("@不能在第一位");
continue;
} else if (email.lastIndexOf(".")==email.length()-1) {
System.out.println(".不能再最后一位");
continue;
} else if (email.indexOf("@")-email.indexOf(".")==1||email.indexOf(".")-email.indexOf("@")==1) {
System.out.println("@和.中间应该有字符");
continue;
}else {
flag = false;
}
}
}
}
public class StringBufferMethod {
public static void main(String[] args) {
/**
* 知识点:SrtingBuffer的使用
*/
StringBuffer sb = new StringBuffer();
sb.append("123abc");//在末尾追加字符串
sb.append("DEF123");//在末尾追加字符串
sb.insert(6,"xyz");//将子字符串插入到此字符串指定下标的位置
sb.setCharAt(6,'X');//替换指定下标上的字符
sb.replace(3,9,"ABCABC");//从开始下标处替换到结束下标处(排他字符串)
sb.deleteCharAt(6);//删除指定下标上的字符
sb.delete(3,11);//从开始下标处(包含)到结束下标处(排他)
sb.reverse();//反转字符串
int len = sb.length();
System.out.println("字符串长度为"+len);
System.out.println(sb);
}
}
public class StringBuilderMethod {
public static void main(String[] args) {
/**
* 知识点:StringBuilder的使用
* 注意:在调用方法的层面上StringBuilder和StringBuffer是一样的
*/
StringBuilder sb = new StringBuilder();
sb.append("123abc");//在末尾追加字符串
sb.append("DEF123");//在末尾追加字符串
sb.insert(6,"xyz");//将子字符串插入到此字符串指定下标的位置
sb.setCharAt(6,'X');//替换指定下标上的字符
sb.replace(3,9,"ABCABC");//从开始下标处替换到结束下标处(排他字符串)
sb.deleteCharAt(6);//删除指定下标上的字符
sb.delete(3,11);//从开始下标处(包含)到结束下标处(排他)
sb.reverse();//反转字符串
int len = sb.length();
System.out.println("字符串长度为"+len);
System.out.println(sb);
}
}
面试题:描述下列代码会创建几个String对象?
答案:1个(常量池中的数据不允许重复)
String str1 = “abc”;
String str2 = “abc”;
面试题:描述下列代码会创建几个String对象?
答案:3个(new了两个String对象+“abc”)
String str1 = new String(“abc”);
String str2 = new String(“abc”);
注:
1.StringBuffer和StringBuilder都是继承的AbstractStringBuilder
2.StringBuffer和StringBuilder的核心功能都是由父类使用
3.StringBuffer和StringBuilder在方法中上锁、解锁 – 在多线程下使用
4.StringBuffer和StringBuilder相比,因为有了上锁和解锁的步骤,所以效率没有StringBuilder高
StringBuilder和StringBuffer的构造方法:
StringBuilder/StringBuffer代表可变的字符序列
StringBuilder/StringBuffer称为字符串缓冲区,
它的工作原理是:预先申请一块内存(char[] value = new char[]),存放字符序列。如果字符序列满了,会重新改变缓冲区的大小,以容纳更多的字符序列(扩容机制:)
StringBuilder/StringBuffer是可变对象,因为char[] value = new char[];
String是不可变对象
//获取自1970年1月1日0:0:0到现在的毫秒数(1000毫秒=1秒)
long currentTimeMills = System.currentTimeMillis();
26.正则表达式
理解:符合某个语句规范的字符串
public static void main(String[] args) {
/**
* 把一个字符串中带电话号码替换成130**1111
* 小结:利用正则表达式做替换功能
*/
String str = "小红13012341111 小绿13012342222 小黑13012343333";
String regex = "(1\\d{2})(\\d{4})(\\d{4})";
str = str.replaceAll(regex,"$1****$3");
System.out.println(str);
}
public static void main(String[] args) {
/**
* 案例:检验QQ邮箱
*/
String email = "14455840@qq.com";
String regex = "\\d{4,11}@qq.com";
boolean matches = email.matches(regex);
System.out.println(matches);
}
public static void main(String[] args) {
/**
* 分隔路径
*/
String str = "C:\\资源\\xx.jpg";
//:?表示冒号出现一次或0次
String regex = ":?\\\\";
String[] split = str.split(regex);
for (String string:split){
System.out.println(string);
}
}
27.日期时间类
理解:日期时间类
Date(java.util) --日期类
SimpleDateFormat --格式化日期类
Calendar --日历类
public static void main(String[] args) throws ParseException {
/**
* Date(java.util) --日期类
* SimpleDateFormat --格式化日期类
* Calendar --日历类
*/
//日期类
Date date = new Date();
//Thu Mar 14 17:04:48 CST 2024
System.out.println(date);
SimpleDateFormat sdf= new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
//格式化日期
//Date转换成String
String datetime = sdf.format(new Date());
System.out.println(datetime);
//String转Date
Date date1 = sdf.parse("2024年03月14日17时15分42秒");
System.out.println(date1);
//获取当前系统的日历对象
Calendar c = Calendar.getInstance();
// int year = c.get(1);
int year = c.get(Calendar.YEAR);
// int month = c.get(2)+1; //月份0-11,0表示1月,所以加个1
int month = c.get(Calendar.MONTH)+1; //月份0-11,0表示1月,所以加个1
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
int second = c.get(Calendar.SECOND);
System.out.println(year);
System.out.println(month);
System.out.println(hour);
System.out.println(minute);
System.out.println(second);
}
28.Math类及静态导入
理解:Math类提供了一系列基本数学运算个几何函数的方法
Math类是final类,并且它的所有成员变量和成员方法都是静态的
面试题:Math.abs会出现负数吗?
System.out.println(Math.abs(-100));
会出现负数–> -214748364
System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MIN_VALUE);//-214748364 System.out.println(Math.abs(Integer.MIN_VALUE));
静态导入**:将类中所有的静态属性和方法都导入到本类来,作为自己的静态属性和方法。
缺点:如果类中的方法和静态导入类中的方法重名了,会调用本类自己的静态方法所以可读性差,不建议使用。
29.Random
public static void main(String[] args) {
/**
* 理解:随机类
*/
Random random = new Random();
System.out.println("随机取出int取值范围里的数值:"+random.nextInt());
System.out.println("随机取出0~9的int数值:"+random.nextInt(10));
System.out.println("随机出double取值范围里面的数值:"+random.nextDouble());
System.out.println("随机出boolean取值范围里的数值:"+random.nextBoolean());
}
30.System类
理解:Java程序的运行平台
1.System类提供了一些静态属性和方法,允许通过类名直接调用
2.System类提供了代表标准输入、标准输出、错误输出的类属性
3.System类提供了一些静态方法用于访问环境变量、系统属性的方法。
public static void main(String[] args) {
//获取系统标准的输入流 - 方向:控制台->程序
InputStream in = System.in;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入int值:");
int num = scanner.nextInt();
//获取系统标准的输出流 - 方向:程序 -> 控制台
PrintStream out = System.out;
out.println(num);
//获取系统标准的错误输出流
PrintStream err = System.err;
err.println(num);
//关闭
scanner.close();
}
public static void main(String[] args) {
/**
* 方法
*/
long currentTimeMills = System.currentTimeMillis();
System.out.println("获取自1970年1月1日到现在的毫秒数:"+currentTimeMills);
//获取系统参数
Properties properties = System.getProperties();
System.out.println("系统参数:"+properties);
//通过具体key获取对应的value
String property = System.getProperty("os.name");
System.out.println("通过具体的key获取对应的value:"+property);
//拷贝数组
int[] arr = {1,2,3,4,5,6,7,8,9,10};
int[] newarr = new int[4];
//(源数组,开始下标,目标数组,开始下标,拷贝长度)
System.arraycopy(arr,3,newarr,0,4);
for (int num:newarr){
System.out.println(num);
}
//利用System.arraycopy做数组的删除功能
String[] names = {"aaa","bbb","ccc","ddd","eee","fff",null,null,null};
System.arraycopy(names,2,names,1,4);
names[5]=null;
for (String str:names){
System.out.println(str);
}
}
31.Runtime类
理解:Runtime表示运行时系统(JVM)
public static void main(String[] args) {
//获取运行时对象
Runtime run = Runtime.getRuntime();
System.out.println("获取最大操作数:"+run.availableProcessors());
System.out.println("获取最大内存数(byte):"+run.maxMemory());
System.out.println("获取闲置内存数(byte):"+run.freeMemory());
}
32.大数值运算类
1)BigInteger – 整数大数值运算类
注意:
1)小数做运算会失去精度,所以小数做运算都要生成大数值的运算类
2)构造方法里的值使用字符串
BigDecimal big1 = new BigDecimal("0.5");
public static void main(String[] args) {
BigInteger big1 = new BigInteger("2100000000");
BigInteger big2 = new BigInteger("2100000000");
BigInteger add = big1.add(big2);
System.out.println("加法:"+add);
BigInteger subtract = big1.subtract(big2);
System.out.println("减法:"+subtract);
BigInteger multiply = big1.multiply(big2);
System.out.println("乘法:"+multiply);
BigInteger divide = big1.divide(big2);
System.out.println("除法:"+divide);
}
public static void main(String[] args) {
BigDecimal big1 = new BigDecimal("2100000000");
BigDecimal big2 = new BigDecimal("2100000000");
BigDecimal add = big1.add(big2);
System.out.println("加法:"+add);
BigDecimal subtract = big1.subtract(big2);
System.out.println("减法:"+subtract);
BigDecimal multiply = big1.multiply(big2);
System.out.println("乘法:"+multiply);
BigDecimal divide = big1.divide(big2);
System.out.println("除法:"+divide);
}
33.枚举
概念:枚举(enum)全称为enumeration,是JDK1.5中引入的新特性。
语法:
public enum Color{
//默认添加public static final
RED,GREEN,BLUE
}
本质:
尽管枚举看起来像是一种新的数据类型,实际上,枚举就是一种受限制的类,并且具有自己的方法。创建自己的enum类时,这个类继承自Java.lang.Enum.
public abstract class Enum<E extends Enum<E>> implements Comparable<E>,serializable{
}
特点:
- 枚举就是一个受限制的类,默认继承Enum.
- 枚举的第一行必须定义该枚举类型对象
- 枚举类型对象默认添加public static final 类型
- 枚举没有继承明确类(自定义枚举类默认继承Enum,Enum默认继承Object)
- 枚举类不能被继承(即枚举不能有子类,一种情况除外,策略枚举)
- 枚举里可以有构造方法、成员方法、静态方法、抽象方法
- 枚举可以实现接口
- 枚举里没有定义方法,可以在最后一个对象后面加逗号,分号或什么都不加
优势:
- 增加代码可读性
- 枚举型可直接与数据交互
- switch语句优势
- 编译优势(枚举类编译时,没有把常量值编译到代码中,即使常量值发生改变,也不会影响引用常量的类)
- 将常量组织起来,统一管理
- 去除equals两者判断 由于常量值地址唯一,使用枚举可以直接通过“==”进行两个值之间的对比,性能会有所提高。
注:1)枚举就是一个特殊的类,但它也是引用数据类型的一种。
2)枚举没有显示继承
3)枚举有隐式继承 ->Season extends Enum extends Object
4)枚举第一行必须声明对象,枚举对象默认使用public static final修饰。
5)枚举的构造方法都是私有的
**组织枚举:**一般使用接口去组织多个枚举
原因:
使用类去组织枚举,类里的枚举默认添加static修饰 – 静态内部类
使用接口去组织枚举,接口的枚举默认添加public static修饰 – 接口内部类(公有静态内部类)
考虑到其它包中还可以使用到枚举对象,所以推荐使用接口去组织
34.集合:
含义:
1.集合是Java API所提供的一系列类,可以用于动态存放多个对象(集合只能存对象)
2.集合与数组的不同在于,集合是大小可变的序列,而且元素类型可以不受限定,只要是引用类型。(集合中不能存放基本数据类型,但可以存放基本数据类型的包装类)
3.集合类全部支持泛型,是一种数据安全的用法。
4.集合既要存储数据,也要管理数据(增删改查)。
集合与数组的区别:
数组:一旦初始化长度不可变,元素类型受限定(String类型的数组只能装String的数据),数组可以存储基本数据类型
集合:长度可变的序列,元素类型不受限定(一个集合可以存储多个数据类型的元素),集合只能存储引用数据类型。
泛型:
含义:泛型是一种数据安全的做法
E - element - 元素
T - type - 类型
K - key - 键
V - value - 值
注意:
1)泛型可以设置多个,使用逗号隔开
2)泛型只在编码阶段有效,不会编译到class文件中
3)泛型只支持引用数据类型
ps: public class HashMap<K,V>{}
泛型限定:规定了泛型类型的范围
1)ArrayList<?>: ?表示任何类型
2)ArrayList<? extends A>: ? extends A:表示A类或其子类
3)ArrayList<? super A>: ? super A:表示A类或其父类
Collection家族:
Collection分类:
1)List接口
2)Set接口
List接口:
特点:有序且可重复(因为List接口中添加了许多针对下标操作的方法)
实现类:
1.ArrayList
2.LinkedList – 队列模式,先进先出
3.Vector
4.Stack
Iterator:
Collection接口下所有的实现类都能获取的迭代器
作用:遍历元素、删除元素
ListIterator:
List接口下所有的实现类才能获取的迭代器
注意:ListIterator继承Iterator
作用:遍历元素、删除元素、替换元素、添加元素、指定下标开始遍历、倒序遍历
List接口 – ArrayList:
public class ArrayListMethod {
/**
* 知识点:ArrayList的使用
* 注意:集合一旦定义了泛型,就规定了集合存储元素的类型
* 所以说泛型是一种数据安全的做法。
* @param args
*/
public static void main(String[] args) {
//创建ArrayList集合的对象
ArrayList<String> list = new ArrayList<>();
//添加元素(add)
list.add("zhou");
list.add("li");
list.add("hu");
list.add("wangwang");
//设置指定元素的下标(set)
list.set(1,"xxx");
//获取指定下标的元素(get)
String str = list.get(1);
System.out.println("获取指定下标的元素:"+str);
//将元素添加到指定下标的位置
list.add(4,"gaga");
//利用Collections工具类给集合做批量添加(Collections.addAll)
ArrayList<String> newlist = new ArrayList<>();
Collections.addAll(newlist,"aaa","bbb","ccc","ddd");
list.addAll(newlist);
//利用Collection工具类给集合做批量添加(Collections.addAll)
ArrayList<String> newlist2 = new ArrayList<>();
Collections.addAll(newlist2,"xx","yy","zz");
list.addAll(4,newlist2);
//清空集合中所有元素
// list.clear();
//判断集合中是否包含指定元素(contains)
boolean b = list.contains("gaga");
System.out.println("判断集合中是否包含指定元素:"+b);
//判断集合中是否包含子集中所有的元素(containsAll)
boolean b1 = list.containsAll(newlist);
System.out.println("判断子集中是否包含所有的元素:"+b1);
//获取集合中第一次/最后一次出现该元素的下标(indexof,lastindexof)
int index = list.indexOf("xxx");
System.out.println("获取集合中第一次出现该元素的下标:"+index);
int index2 = list.lastIndexOf("xxx");
System.out.println("获取集合中最后一次出现该元素的下标:"+index2);
//判断集合中是否没有元素(isEmpty)
System.out.println("判断集合中是否没有元素:"+list.isEmpty());//false
//删除(remove,retainAll)
//根据下标删除元素
// list.remove(4);
//根据元素删除元素
// list.remove("hu");
//删除list中包含newlist1的元素(去除交集)
// list.retainAll(newlist);
//保留list中包含newlist2的元素(保留交集)
list.retainAll(newlist2);
//截取开始下标(包含)到结束下标处(不包含)处的元素,返回新的list(sublist)
List<String> sublist = list.subList(1,3);
//将集合转成数组
Object[] objects = sublist.toArray();
System.out.println(Arrays.toString(objects));
//将集合转成数组
String[] ss = new String[2];
sublist.toArray(ss);
System.out.println(Arrays.toString(ss));
System.out.println("----------------");
int size = list.size();
System.out.println("获取元素个数:"+size);
//遍历数据 --for循环
for (int i=0;i< list.size();i++){
System.out.println(list.get(i));
}
//遍历数据 --foreach
for (String element:list){
System.out.println(element);
}
System.out.println("--------------");
//遍历数据 --Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()){//判断是否有可迭代的元素
String element = it.next();
System.out.println(element);
}
System.out.println("--------------------");
//遍历数据 -- ListIterator
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()){
String element = listIterator.next();
System.out.println(element);
}
}
ArrayList的数据结构是什么?
--Object类型的一维数组
ArrayList默认初始化容量是多少?
10
ArrayList最大容量是多少?
Integer.MAX_VALUE-8
ArrayList最大容量为什么是Integer.MAX_VALUE-8
减8是为了腾出空间存放的头部信息
ArrayList扩容机制是什么?
扩容后的长度是原来长度的1.5倍
如何减少集合的伸缩性?
根据需求判断元素大概的长度,在创建集合时指定长度,减少扩容次数,提高效率。
List接口 – LinkedList:
定义:LinkedList<> list = new LinkedList<>();
注意:LinkedList和ArrayList使用是一模一样的,这要归功于Collection接口和list接口,因为接口是定义标准的,只不过LinkedList和ArrayList底层实现原理是不一样的,LinkedList底层使用双向链表去实现对数据的操作(增删改查)。
LinkedList独有的方法:
public class LinkedListOne {
public static void main(String[] args) {
/**
* 知识点:LinkedList独有的方法
*/
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("aaa");
linkedList.add("abc");
linkedList.add("bbb");
linkedList.add("abc");
linkedList.add("ccc");
linkedList.add("ddd");
//将元素添加到首部
linkedList.addFirst("xxx");
linkedList.offerFirst("eee");
linkedList.push("head");
//将元素添加到尾部
linkedList.addLast("yyy");
linkedList.offerLast("fff");
linkedList.offer("hhh");
//获取第一个/最后一个元素
System.out.println("获取第一个元素:"+linkedList.element());
System.out.println("获取第一个元素:"+linkedList.getFirst());
System.out.println("获取第一个元素:"+linkedList.peek());
System.out.println("获取第一个元素:"+linkedList.peekFirst());
System.out.println("获取最后一个元素:"+linkedList.getLast());
System.out.println("获取最后一个元素:"+linkedList.peekLast());
//删除第一个元素
linkedList.pop();
linkedList.poll();
linkedList.pollFirst();
linkedList.removeFirst();
//删除最后一个元素
linkedList.pollLast();
linkedList.removeLast();
//删除第一次出现的元素
linkedList.removeFirstOccurrence("abc");
//删除最后一次出现的元素
linkedList.removeLastOccurrence("abc");
System.out.println("--------倒序遍历----------");
//倒序遍历
Iterator<String> descendingIterator = linkedList.descendingIterator();
while (descendingIterator.hasNext()){
String element = descendingIterator.next();
System.out.println(element);
}
System.out.println("-----------遍历-----------");
for (String element:linkedList){
System.out.println(element);
}
}
}
List接口 – Vector:
Vector和ArrayList使用是一模一样的,这要归功于Conllection和list接口,只不过Vector和ArrayList底层实现是不一样的,Vector底层使用一维数组+线程安全去实现对于数组的操作(增删改查)。
public class VectorOne {
public static void main(String[] args) {
Vector<String> v = new Vector<>();
v.addElement("aaa");
v.addElement("bbb");
v.addElement("ccc");
v.addElement("ddd");
v.addElement("eee");
//清空
// v.removeAllElements();
//根据下标删除元素
v.removeElementAt(0);
//根据元素删除元素
v.removeElement("ccc");
//将元素设置到指定下标的位置
v.setElementAt("xxx",1);
Enumeration<String> elements = v.elements();
while (elements.hasMoreElements()){
elements.nextElement();
System.out.println(elements);
}
}
}
List接口 – Vector – Stack:
研究Stack的特点 – 栈模式(先进后出)
注意:
1)该类继承Vector,所以Stack可以使用父类所有的非私有方法
public class StackOne {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
//将元素添加到栈里
stack.push("111");
stack.push("222");
stack.push("333");
stack.push("444");
stack.push("555");
System.out.println("获取栈顶元素:"+stack.peek());
int search = stack.search("222");
System.out.println("获取元素距离栈顶的个数(基于1)"+search); //4
//遍历取出元素
while (!stack.empty()){
//删除栈顶元素,并返回
String element = stack.pop();
System.out.println(element);
}
}
}
Set接口:
特点:无序且不可重复
实现类:
1.HashSet
2.LinkedHashSet
3.TreeSet
Set接口 – HashSet:
注意:HashSet实现Set接口,Set接口继承Collection接口
ArrayList实现List接口,List接口继承Collection接口
因为ArrayList里的方法除了针对下标操作的,其余的都在Set接口中
特点:无序,去重。
无序的原因:存入顺序的规则和取出顺序的规则不一致
去重的原因:元素的hash值相同,再判断hash && == || equals,如果相同就不存入到集合中。
for (String element : set){ System.out.println(element);}
存入顺序:
1.获取元素的hash值 – hashCode()
2.通过hash值计算在数组中的下标
3.判断下标上是否有元素
没有–直接添加元素
有–判断元素是否相同(hash值 && == || equals)
是相同–不存储(达到了去重的效果)
不是 – 添加元素(JDK1.7头插法,JDK1.8尾插法)
取出顺序:
遍历数组 + 单向链表
Set接口–LinkedHashSet:
特点:有序,去重。
有序的原因:在HashSet的基础上添加了双向链表
去重的原因:元素的hash值相同,再判断hash&&==||equals,如果相同就不存入到集合中。
存入顺序:
1.获取元素的hash值
2.计算在数组中的下标
3.判断下标上是否有元素
没有 – 直接添加(记录上一个添加元素的地址,上一个添加的元素会记录在元素的地址,双向链表)
有–判断元素之间是否相同
相同:就不添加(去重)
不同:就添加元素(JDK1.7头插法,JDK1.8尾插法)
Set接口 – TreeSet:
TreeSet特点:自然排序
public class TreeSetOne {
/**
* TreeSet的特点 -- 自然排序
* 理解:TreeSet会根据元素类型的不同自动选择排序规则
*/
public static void main(String[] args) {
//TreeSet存储Integer的排序规则:数字升序
System.out.println("-----------Integer存储-------------");
TreeSet<Integer> set = new TreeSet<>();
set.add(20);
set.add(10);
set.add(40);
set.add(30);
set.add(50);
for (Integer element : set){
System.out.println(element);
}
System.out.println("-----------String存储------------");
//TreeSet存储String的排序规则:字典排序
TreeSet<String> set2 = new TreeSet<>();
set2.add("b");
set2.add("d");
set2.add("a");
set2.add("c");
set2.add("e");
for (String element:set2){
System.out.println(element);
}
}
}
Map家族:
注意:
1.HashMap的key不允许重复,key是唯一的
2.HashMap的value允许重复
HashMap的特点: 无序 + key去重
Map – HashMap:
注意:put方法既是添加,也是替换
/**
* HashMap的使用
*/
public class HashMapOne {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
//添加元素
map.put("xxx",20);
map.put("yyy",21);
map.put("zzz",20);
map.put("aaa",19);
map.put("bbb",22);
//将newMap中的所有元素添加到map集合中(putall)
HashMap<String,Integer> newmap = new HashMap<>();
newmap.put("a",10);
newmap.put("b",20);
newmap.put("c",22);
map.putAll(newmap);
//清空集合中的元素
//map.clear();
//通过key删除映射关系
map.remove("bbb");
//通过key+value删除映射关系(key+value)
map.remove("aaa",19);
//通过key替换value
map.replace("xxx",30);
//通过Key+value替换value
map.replace("xxx",30,22);
//获取映射关系个数
int size = map.size();
System.out.println("获取映射关系的个数:"+size);
//获取map中所有的value
Collection<Integer> values = map.values();
System.out.println(Arrays.toString(values.toArray())); //将集合转换为数组,再将数组转换为字符串
System.out.println("--------------------");
//通过Key获取到对应的value值,如果不存在,就不添加
Integer integer = map.get("xxx");
System.out.println("通过key获取到对应的value值:"+integer);
System.out.println("------------------");
//遍历 -- keySet()
//思路:获取map集合中的所有的key放在一个Set集合中,遍历Set集合获取key,
Set<String> keySet = map.keySet();
for (String key :keySet){
Integer value = map.get(key);
System.out.println(key + "--"+value);
}
}
}
面试题:
针对HashMap的value排序
/**
* HashMap面试题:针对于HashMap的value排序
* 1)转成Set集合
* 2)转成ArrayList集合
* 3)利用ArrayList的sort集合去排序
* 遍历ArrayList
*/
public class HashMapThree {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("xxx",20);
map.put("yyy",21);
map.put("zzz",22);
//将map的映射关系对象取出,返回Set集合
Set<Map.Entry<String, Integer>> entries = map.entrySet();
//将Set集合转换成ArrayList集合
ArrayList<Map.Entry<String, Integer>> list = new ArrayList<>(entries);
//利用ArrayList的sort方法去排序
list.sort(new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
Integer v1 = o1.getValue();
Integer v2 = o2.getValue();
return Integer.compare(v1,v2);
}
});
//遍历ArrayList
for (Map.Entry<String,Integer> entry:list){
System.out.println(entry);
}
}
}
Map – HashMap – LinkedHashMap:
注意:LinkedHashMap是HashMap的子类,HashMap如何使用,LinkedHashMap就如何使用!!
特点:
1)有序+key去重
Map – Hashtable:
特点:无序 + key去重
Map – ConcurrentHashMap:
特点:无序+ key去重
HashMap vs LinkedHashMap vs Hashtable vs ConcurrentHashMap:
区别一:存储null键的情况
区别二:应用场景的区别:
HashMap:无序+key去重
LinkedHashMap:有序+key去重
Hashtable:无序+key去重+线程安全(在方法中加锁,效率低,已弃用)
ConcurrentHashMap:无序+key去重+线程安全(局部加锁+CAS实现线程安全,效率高,多线程下使用ConcurrentHashMap)
Map – TreeMap:
注意:外置比较器 > 内置比较器
Map – Hashtable – Properties:
public class PropertiesOne {
public static void main(String[] args) throws IOException {
//创建配置文件对象
Properties p = new Properties();
//将配置文件加载到配置文件对象中
p.load(PropertiesOne.class.getClassLoader().getResourceAsStream("db.properties"));
//获取配置文件里的数据
String username = p.getProperty("username");
String password = p.getProperty("password");
System.out.println(username +" -- "+password);
//注意1:如果key不存在,返回的为Null
String name = p.getProperty("name");
System.out.println(name);
//注意2:如果key不存在,则返回默认值
String property = p.getProperty("url","默认值");
System.out.println(property);
p.setProperty("author","xxx");
String author = p.getProperty("author");
System.out.println(author);
}
}
Collections:
Collections:集合工具类
注意:Collection与Map的区别:
1.Collection与Map的区别:
Collection存储单个值,可以获取迭代器进行遍历
Map存两个值(key-value),不可以获取迭代器,不能遍历(Map可以间接遍历)
2.理解Set为什么无序:
无序:存入顺序和取出顺序不一样,无序不等于随机。
3.ArrayList与LinkedList的区别:
使用上的区别:
LinkedList添加了
队列模式-先进先出(removeFirst())
栈模式-先进后出(removeLast())
效率上的区别:
ArrayList底层数据结构是一维数组
LinkedList底层数据结构是双向链表
添加-不扩容情况:ArrayList块
添加-扩容情况:LinkedList块
删除:LinkedList块
查询:ArrayList块
35.异常
异常的概念:
异常是程序在运行期间发生的不正常时间,它会打断指令的正常执行流程
Java语言使用异常处理机制为程序提供了异常处理能力
异常分类:
1)错误(Error):JVM系统内部错误或资源耗尽等严重情况–属于JVM需要负担的责任这一类异常无法恢复或不可能捕获,将导致应用程序中断
2)异常(Exception):其它因编程错误或偶然的外在因素导致的一般性问题,这类异常得到恰当的处理时,程序有机会恢复至正常运行状况。
–RuntimeException:非受检性异常:编译器不要求强制处理的异常,一般指编程时的逻辑错误,是程序员应该积极避免其出现的异常。
注意:java.lang.RuntimeException及它的子类都是非受检异常
–一般性异常(受检性异常):编译器要求必须处理的异常,指的是程序在运行时由于外界因素造成的一般性异常。
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=…%2Fjpgs%2F1713325957998.png&pos_id=img-EYOd1GJe-1716433498186
StackOverflowError – 栈内存溢出的错误
出现原因:调用方法会在栈中开辟空间,用于存放该方法的局部变量,死循环的调用方法,栈内存就满载了
ClassCastException:类型转换异常
ClassNotFoundException:类未找到异常
FileNotFoundException:文件未找到异常
异常处理机制:
1)Java程序在执行过程中如果出现异常,会自动生成一个异常类对象,该异常类对象将自动提交给JVM,这个过程称为抛出(throw)异常
2)当JVM接受到异常对象时,会寻找能处理这一异常的代码
--找到了 - 把当前异常对象交给其处理,这一过程称为捕获(catch)异常和处理异常
--没找到 - 运行时系统将终止,相应的Java程序也将退出
Java处理异常的能力:
1)try…catch… :处理一个异常的场景
try {
…可能发生异常的代码…
}catch(异常类型 e){
//捕获异常
…处理异常…
}finally{
…不管是否发生异常,都会执行的代码块…
}
2)try…catch…catch…:处理多个异常的场景
注:
- try…catch…中可以有多个catch
- finally代码块可写可不写
- 先捕获的异常范围不能大于后捕获的异常范围
2)throws
语法结构:public void method() throws 异常1,异常2{}
3)throw
语法结构:throw new 异常()
理解:
手动抛出异常
throw结合自定义异常使用
36.多线程:
进程:
进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源
单进程操作系统和多进程操作系统的区别:
单进程操作系统:dos(一瞬间只能执行一个任务)
多进程单用户操作系统:Window(一瞬间只能执行多个任务)
多进程多用户操作系统:Linux(一瞬间能执行多个任务)
现在的多核CPU是否可以让系统在同一个时刻可以执行多个任务吗?
理论上是可以的
什么是线程,理解线程和进程的关系
什么是线程?
线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源
一个进程可以有多个线程,各个线程有不同的分工
理解线程与进程的关系
进程与进程之间的关系:进程之间的内存空间和系统资源是独立的
同一个进程里的多条线程:线程之间的内存空间和系统资源是共享的
进程里:可以有一条或一条以上的线程
进程里只有一条线程的情况下,只有一条线程叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
Ps:线程是在进程里的,他们是包含关系
小结:
进程与进程的关系:独享内存空间和系统资源
线程与进程的关系:有一个进程中至少包含一个线程
线程与线程的关系:在同一个进程里,多个线程共享内存空间和系统资源
一个进程包含多个线程,只有一个主线程
经典面试题:请问当我们编写一个单纯的main方法时,此时该程序是否未单线程的?为什么?
多线程,因为垃圾回收器是一个多线程
线程的优先级别:
需求:在主线程中创3个子线程,并且设置不同优先级,观察其优先级对线程执行结果的影响。
a.setPriority(Thread.MAX_PRIORITY)
b.setPriority(Thread.NORM_PRIORITY)
c.setPriority(Thread.MIN_PRIORITY)
线程的休眠:
Thread的静态方法:
Thread.sleep(毫秒);
线程的礼让:
礼让:让当前线程退出CPU资源,并转为就绪状态,接着再抢
Thread.yield()
线程的合并:
t.join() 合并方法
public static void main(String[] args) throws Integer{
MyThread t = new MyThread();
t.start();
for(int i=0;i<=200;i++){
System.out.println("主线程:"+i);
if(i == 10){
//让线程加入到当前线程
t.join();
}
}
}
线程的中断:
t.stop() //立刻停止,可能会造成功能的缺失
线程的生命周期:
1)新建状态
i.在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。
ii.例如:Thread thread = new Thread();
2)就绪状态
i.新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动后,线程就会进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。
3)运行状态
4)阻塞状态
5)消亡状态
线程安全:
铁道部发布了一个售票任务,要求销售1000张票,要求有三个窗口来进行销售,请编写多线程程序效果(该题涉及到线程安全)
i.窗口001正在售第一张票
ii.窗口002正在售第2张票
iii.窗口003正在售第3张票
…
v.窗口002正在售第1000张票
涉及到线程安全,要加锁
任务:使用任务类的方式解决买票的需求(要求:加锁的方式使用同步代码块,同步方法,Lock)
线程安全 – 单例模式(懒汉式)
注意:单例模式(懒汉式)不是线程安全的
线程安全 – 单例模式(饿汉式)
注意:单例模式(饿汉式)是线程安全的
缺点:如果只调用了类里的静态方法,没用到单例对象,就是浪费空间
双重检验锁:
volatile – 防止指令重排
创建对象的过程:
1)开辟空间 ---- new 对象() --0x001
2)调用构造方法 – 初始化数据
3)将空间赋值给引用 – 类型 对象名 = 0x001
创建对象的步骤:a/b/c 或 a/c/b
注意:如果创建对象是a/c/b,多线程的情况可能会导致获取的属性为null
解决方案:使用volatile,防止指令重排,创建的步骤必须按照a/b/c
线程安全 – ArrayList
前言:ArrayList是线程不安全的集合
解决方案1:使用Vector – sychronized锁
解决方案2:使用Collections的sychronizedList方法将AyyayList转换为线程安全的集合
解决方案3:使用CopyOnWriterArrayList – lock锁
线程安全 — 死锁
线程安全 — 可重入锁 :就是一个线程不用释放,可以重复的获取一个锁n次,只是在释放的时候,也需要相应的释放n次。
注意:synchronized同步代码块,synchronized同步方法,Lock锁都是可重入锁
生产消费者模型:重点加锁的逻辑放在线程中
仓储模型:重点加锁的逻辑是放在线程外
线程池:
引入:
一个线程完成一项任务所需时间为:
1.创建县城时间 -Time1
2.线程中执行任务的时间 -Time2
3.销毁线程的时间 -Time3
为什么销毁线程池:
1.线程池技术正是关注如何缩短和调整Time1和Time3的时间,从而提高程序的性能,项目中可以把Time3,Time1分别安排在项目的启动和结束的时间段或者一些空闲的时间段
2.线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建线程的数目,提高线程的复用率
3.系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情况下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池
Java提供的线程池:
ExecutorSrevice:线程池的接口
Executors:创建各种线程池的工具类
注:一般不会用这两个线程池,用自定义的线程池。
public class Test01 {
public static void main(String[] args) throws InterruptedException {
//获取单个线程的线程池
// ExecutorService pool = Executors.newSingleThreadExecutor();
//获取指定个数的线程池
// ExecutorService pool = Executors.newFixedThreadPool(3);
//获取可缓存的线程池(60s没有工作的线程认为是闲置线程,就会被线程池回收/销毁)
// ExecutorService pool = Executors.newCachedThreadPool();
//获取延迟任务的线程池
ExecutorService pool = Executors.newSingleThreadScheduledExecutor();
//设置延迟时间
pool.awaitTermination(6, TimeUnit.SECONDS);
//循环创建任务对象,并提交给线程池
for (int i=1;i<100;i++){
//创建任务对象
Task task = new Task(i);
//提交任务
pool.execute(task);
}
//关闭线程池
pool.shutdown();
}
}
public class Task implements Runnable{
private int num;
public Task() {
}
public Task(int num){
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"处理了第"+num+"个任务");
}
}
/**
* 知识点:无界队列
* 理解:这个队列没有上限
*
* 继承关系:LinkedBlockingQueue -> AbstractQueue ->AbstractCollection
* 小结:
* 1.LinkedBlockingQueue是Collection集合家族的一员
* 2.Collection集合家族(List,Set,Queue)
* 3.LinkedBlockingQueue数据结构单项链表
* 缺点:可能造成内存溢出
*/
public class Queue {
public static void main(String[] args) throws InterruptedException {
//创建无界队列
LinkedBlockingDeque<String> queue = new LinkedBlockingDeque<>();
//添加元素
queue.put("aaa");
queue.put("bbb");
queue.put("ccc");
queue.put("ddd");
queue.put("eee");
queue.put("fff");
//删除元素
queue.remove("ccc");
//遍历队列
Iterator<String> it = queue.iterator();
while (it.hasNext()){
Object string = it.next();
System.out.println(string);
}
}
}
/**
* 知识点:有界队列
* 理解:这个队列有上限
*
* 继承关系:ArrayBlockingQueue -> AbstractQueue ->AbstractCollection
* 小结:
* 1.LinkedBlockingQueue是Collection集合家族的一员
* 2.Collection集合家族(List,Set,Queue)
* 3.LinkedBlockingQueue数据结构一维数组
*
*/
public class BoundedQueue {
public static void main(String[] args) throws InterruptedException {
//创建有界队列
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(6);
//添加元素
queue.put("aaa");
queue.put("bbb");
queue.put("ccc");
queue.put("ddd");
queue.put("eee");
queue.put("fff");
//删除元素
queue.remove("ccc");
//遍历队列
Iterator<String> it = queue.iterator();
while (it.hasNext()){
Object string = it.next();
System.out.println(string);
}
}
}
/**
* 优先队列
*/
public class PriorityQueue {
public static void main(String[] args) {
//创建优先队列
PriorityBlockingQueue<Priority> queue = new PriorityBlockingQueue<>();
//添加元素
queue.put(new Priority(3));
queue.put(new Priority(1));
queue.put(new Priority(4));
queue.put(new Priority(2));
//遍历取出队列
while (!queue.isEmpty()){
Priority priority = queue.poll();
System.out.println(priority.getPriority());
}
}
}
public class Priority implements Runnable,Comparable<Priority>{
private int priority;
public Priority(int priority) {
this.priority = priority;
}
//当前对象和其它对象作比较,当前优先级别大就返回-1,优先级别小就返回1,值越小优先级越高
@Override
public int compareTo(Priority o) {
return Integer.compare(this.priority, o.priority);
}
@Override
public void run() {
System.out.println("任务被处理了");
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
拒绝策略:
ThreadPoolExecutor自带的拒绝策略有四种,都实现了RejectedExcutionHandler接口
比如:new ThreadPoolExecutor.AbortPolicy()
自定义线程原因:
面试题:线程池中核心线程会被回收吗?
当线程池调用了allowCoreThreadTimeOut(true);时,核心线程会被回收,但是一般不会调用该方法,也就是说,项目中线程池中的核心线程不要设计被回收,因为如果线程池中线程全部被回收了,就没有线程复用率这个说法了。
ThreadPoolExectuor扩展:
ThreadPoolExectuor扩展最主要是围绕beforeExecute(),afterExecute()和terminated()三个方法的重写,通过这三个方法我们可以监控每个任务的开始时间和结束时间,或者其它一些功能,下面我们可以通过代码实现一下。
面试题:
1.使用Java自带的线程池,还是自定义线程池
2.为什么使用Java自带的线程池,或者为什么使用自定义线程池
3.自定义线程池参数有哪些?
核心线程数,最大线程数,任务队列,拒绝策略/饱和策略,闲置时间,时间单位,线程工厂
4.怎么设置最大线程数,核心线程数?
实际工作中可以使用sysbench多线程性能测试工具,测试出来多少个,按照用户的比例放大。
37.File类
File,是文件和目录路径名的抽象表示,File可以表示一个文件,也可以表示一个路径
表示文件:C:Users\xxx\Desk\hhy.txt
表示路径:C:Users\xxx\Desk
File只关注文件本身的信息(文件名,文件大小,可读可写…),而不能操作文件里面的内容
File类 – 表示文件或文件夹,不能对文件里的数据进行操作
对文件里的数据进行操作的是:IO流
以应用驱动对API的学习:
需求1:
需求1:通过程序,获取已知文件的以下信息:
对象 – 文件
File构造方法
1)该文件存放的绝对路径 String getAbsolutPath()
2)该文件的文件名String getName();
3)该文件是否可写boolean canWrite();
4)该文件是否可读 boolean canRead();
5)该文件是否为隐藏文件 boolean isHidden();
6)该文件的长度 int length();
相对路径与绝对路径:
绝对路径:从根目录开始的完整路径,包含了文件系统中该文件或目录所在的完整路径,通常以根目录符号(如Linux中的“/”,windows中的“C:\”)开始
相对路径:相对于当前项目下的路径
public class FilePath {
public static void main(String[] args) {
File file = new File("file.txt");
System.out.println(file.length());
//D:\IDEA_Maven2022\qf_study\file.txt
System.out.println("绝对路径"+file.getAbsolutePath());
//file.txt
System.out.println("相对路径"+file.getPath());
}
}
需求2:
通过程序,判断指定路径的文件是否存在,如果不存在,则创建该文件下面按各种情况来解决该问题
1)目录已存在的情况
2)有一个层级的目录不存在的情况
3)有多个层级的目录不存在的情况
public class FileTwo {
public static void main(String[] args) throws IOException {
// File file = new File("Files\\zjj.txt");
File file = new File("Files\\File\\zjj.txt");
System.out.println(file.getAbsolutePath());
//获取父路径
File parentFile = file.getParentFile();
//判断父路径是否存在
if (!parentFile.exists()){
//创建一层目录
// parentFile.mkdir();
//创建多层目录
parentFile.mkdirs();
}
//判断文件是否存在
if (!file.exists()){
file.createNewFile();
}
}
}
需求3:
需求3:输出指定目录下的所有文件信息,比如打印当前项目根目录下的所有文件信息(只考虑当前目录,不考虑子目录)
改进:
1)要求只输入文件后缀名为txt的文件
2)根据API的过滤器来完成该功能
3)需求继续跟进,列出当前目录及子目录中符合该条件的文件信息(递归)
public class FileThree {
public static void main(String[] args) {
File file = new File("D:\\大三作业空间");
//输出指定目录下的所有文件信息 -- 法一
//获取当前目录下所有文件及文件夹的名字
// String[] list = file.list();
// for (String fileName:list){
// System.out.println(fileName);
// }
//-- 法二
//获取当前目录下的所有File对象
File[] listFiles = file.listFiles();
for (File f:listFiles){
System.out.println(f.getName()+"--"+f.canRead()+"--"+f.canWrite());
}
public class FileThree {
public static void main(String[] args) {
File file = new File("D:\\大三作业空间");
//要求1:要求只输入文件后缀名为txt的文件 --法一
//获取当前目录下所有文件及文件夹的名字
// String[] list = file.list();
// for (String fileName:list){
// //判断的是文件和文件夹
// if (fileName.endsWith(".txt")){
// System.out.println(fileName);
// }
// }
// --法二
//获取当前目录下的所有File对象
File[] listFiles = file.listFiles();
//获取当前目录下的所有文件及文件夹的file对象
for (File f:listFiles){
String fileName = f.getName();
//判断f是否是文件
if(f.isFile() && fileName.endsWith(".txt")){
System.out.println(f.getName());
}
}
}
}
public class FileThree {
public static void main(String[] args) {
File file = new File("D:\\大三作业空间");
//要求2:根据API的过滤器来完成该功能
String[] list = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File childFile = new File(dir,name);
if (childFile.isFile() && childFile.getName().endsWith(".txt")){
return true;
}
return false;
}
});
for (String fileName:list){
System.out.println(fileName);
}
}
}
public class FileThree {
public static void main(String[] args) {
File file = new File("D:\\大三作业空间");
//要求3:需求继续跟进,列出当前目录及子目录中符合该条件的文件信息(递归)
fileHandler(file,".txt");
}
public static void fileHandler(File file,String suffix){
File[] listFiles = file.listFiles();
for (File f:listFiles){
if (f.isFile()){//文件
String name = f.getName();
if (name.endsWith(suffix)){
System.out.println(name);
}
} else if (f.isDirectory()) {//文件夹
fileHandler(f,suffix);
}
}
}
}
IO流:
概念:
I – in – 输入(读)
O --out–输出(写)
流 --一点一点的像流水一样去传输数据
注意:站在程序的角度去看待输入还是输出
分类:
按照方向分流;输入流、输出流
按照单位分流:字节流,字符流
按照功能分流:基础流/节点流、处理流
注意:new处理流(new基础流)–装饰者模型–处理流对象中往往包含了基础流的对象,在基础流的基础上拓展了额外的功能,使得流更为强大(效率),工作中一般使用处理流
存储单位:
1024Byte = 1KB
1024KB = 1MB
1024Mb = 1GB
1024GB = 1TB
1024TB = 1PB
注意:进制1024
学习注意事项:
1.按照流的发展历史去学习
2.注意流与流之间的继承关系
3.举一反三
字节流:
字符流:
class BufferedReader extends Reader --带缓冲区的字符输入流
class BufferedReader extends Writer --带缓冲区的字符输出流
默认缓冲区:8192字符 – new char[8192]
各种流:
对象流:
class objectInputStream – 对象输入流
class objectOutputStream – 对象输出流
序列化:将程序中的对象写入到文件
反序列化:将文件里的对象读取到程序中
注意:
1)如果对象像写入文件,对象所属类就必须实现序列化接口(Serializable)
2)Serializable序列化接口没有任何属性和方法,这种接口称之为标记型接口
3)对象所属的类实现了序列化接口一定要添加序列化ID(serialVersionUID)
4)属性使用transient修饰,该属性不会随着对象而写入到文件中。
内存流:
class ByteArrayInputStream – 内存输入流
class ByteArrayOutputStream – 内存输出流
应用场景:项目中频繁使用的数据可以使用内存流备份一份。
打印流:
class PrintStream – 字节打印流
class PrintWriter – 字符打印流
注意:打印流实际上就是输出流,只有一个方向(程序->文件)
重定向:
理解:重新定义系统标准的输入流,输出流,错误输出的方向
System.in:获取系统标准书的流的方向(控制台->程序)
System.out:获取系统标准输出的方向(程序->控制台)
System.err:获取系统标准错误输出流的方向(程序->控制台)
//重定向:重新定义系统标准输入流的方向
System.setIn(new FileInputStream((“io.txt”),true));
//重定向:重新定义系统标准错误输出流的方向(程序->文件)
System.setErr(new FileInputStream((“io.txt”),true))
随机访问流:
理解:该流认为文件是一个大型的byte数组,有一个隐藏的指针(默认为0),其实就是下标,可以从指针的位置写入或读取,意味着该流两个方向。
38.初识网络编程
1.计算机网络
计算机网络 – 连接分散计算机设备以实现信息传递的系统
计算机网络,是指将地理位置不同的具有独立功能的多台i算计及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
2.网络编程
实现多台计算机之间实现数据的共享和传递,网络应用程序主要组成为:
网络编程+IO流+多线程
3.网络模型
两台计算机之间的通信是根据什么规则来走的(OSI & TCP/IP)
4.网络编程三要素
网络通讯的模型:请求-响应,客户端-服务端
三要素:IP地址,端口,协议(数据传输的规则)
5.InetAddress类
用来表示主机的信息
getByName
public static InetAddress getByName(String host) throws UnknownHostException
在给定主机名的情况下确定主机的IP地址
主机名可以是机器名(如Java.sun.com),也可以是其IP地址的文本表示形式,如果提供字面值IP地址,则进检查地址格式的有效性。
getLocalHost
public static InetAddress getLocalHost() throws UnknownHostException
返回本地主机。
6.Socket
Socket也叫套接字,其表示的是IP地址和端口号的组合
网络编程主要就是指Socket编程,网络间的通信其实就是Socket间的通信,数据就通过IO流在两个Socket间进行传递。
7.TCP编程
API:Socket,ServerSocket
客户端(发送一个请求)服务端(接受到这个请求,给予响应)
8.UDP协议
1)UDP协议的Socket是DatagramSocket
2)7070表示的是自己的端口号,不是对方的端口号
39.注解:
1)什么是注解
Annotation是从JDK1.5开始引入的新技术,注解即可以对程序员解释又可以对程序解释
2)注解与注释的区别
注释:对程序员解释代码信息
注解:对程序和程序员解释代码信息
3)注解的作用
1.不是程序本身,可以对程序做出解释(与注释(Comment)类似)
2.可以被其它程序(编译器)读取
3.可以作用于包,类方法,属性上,给他们添加额外的信息
4)注解的格式
注解是以"注释名"在代码中存在的,还可以添加一些参数
例如:@SuppressWarning(value=“unchecked”)
5)注解的应用
可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以随时通过反射机制实现对这些数据的访问。
6)内置注解
Java给我们提供的注解
7)元注解
元注解的作用:负责注解其它注解,Java定义了4个标准的meta-annotation类型,它们被用来对其它annotation类型做说明
这些类型和他们所支持的java.lang.annotation包中可以找到
(@Target,@Retention,@Documented,Inherited)
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存注释该注释信息,用于描述注解的生命周期(SOURCE<CLASS>)
@Document:说明该注解将被包含在javadoc中
@Ingerited:说明子类可以继承父类中的该注解
8)自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
分析:
@interface用来声明一个注解,格式:public @interface注解名{定义内容}
其中的每一个方法实际上是声明了一个配置参数
方法的名称就是参数的名称
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
public class Test01 {
//@Override表示该方法是重写父类的方法
@Override
public String toString(){
return super.toString();
}
//@Deprecated表示该方法已经过时
@Deprecated
public void method01(){
}
//@SuppressWarnings表示镇压警告的注解
@SuppressWarnings("all")
public void method02(){
ArrayList list = new ArrayList();
list.add(100);
list.add(123.123);
list.add("xxxx");
}
}
40.反射
1)Java的反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性喝方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
2)获取Class文件对象
/**
* 知识点:反射
* 前言:
* 使用到一个类,JVM会将该类的class文件加载到方法区(类加载机制)
* 同时会在堆内存中创建该类的class对象,class对象作为class文件的访问入口
* 理解:
* 反射实际上就是获取class对象,通过class对象访问class文件中的内容
* (包括class文件中的所有信息,属性,构造方法,静态方法,成员属性)
* 访问内容:
* 属性
* 构造方法
* 普通方法(成员方法,静态方法,抽象方法...)
* 方法上的参数
* 方法上的返回值
* 数组
* 知识点:利用反射获取class对象
*/
public class Test01 {
/**
*注意:
* 该类的class文件只生在一次到方法区
* 该类的class文件在程序中是唯一的
* 所以不管使用哪种方式获取class对象都是一样的
*/
public static void main(String[] args) throws ClassNotFoundException {
//获取class对象方式1
Class<? extends Student> clazz1 = Student.class;
//获取class对象方式2
Student stu = new Student();
Class<? extends Student> clazz2 = stu.getClass();
//获取class对象方式3
Class<?> clazz3 = Class.forName("day38.reflex01.Student");
//比较两个对象的内存地址是否相同
System.out.println(clazz1==clazz2);
System.out.println(clazz1==clazz3);
}
}
/**
* 知识点:利用反射获取class对象
* 经验:配置文件 + class.forName()
*/
public class Test02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Properties p = new Properties();
p.load(Test02.class.getClassLoader().getResourceAsStream("classPath.properties"));
String path = p.getProperty("path");
Class<?> clazz = Class.forName(path);
System.out.println(clazz);
}
}
3)调用构造函数创建对象
4)反射-操作属性
public class ReflexUtil {
/**
* 获取当前类及其父类的属性对象
* @param clazz class对象
* @param name 属性名
* @return
*/
public static Field getField(Class<?> clazz,String name){
for (Class<?> c = clazz;c!=null;c=c.getSuperclass()){
try {
Field field = c.getDeclaredField(name);
return field;
} catch (NoSuchFieldException e) {
}
}
return null;
}
/**
* 设置对象中的属性
* @param obj 对象
* @param name 属性名
* @param value 属性值
*/
public static void setField(Object obj,String name,Object value){
Field field = getField(obj.getClass(),name);
if (field != null){
field.setAccessible(true);
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
}
}
}
}
public class Test03 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1.拿到对象
Properties p = new Properties();
p.load(Test03.class.getClassLoader().getResourceAsStream("classPath.properties"));
String path = p.getProperty("path");
Class<?> clazz = Class.forName(path);
//获取本类及其父类的公有的属性
Field[] fields = clazz.getFields();
for (Field field:fields){
System.out.println(field);
}
//获取本类所有的属性
Field[] fieldss = clazz.getDeclaredFields();
for (Field field:fieldss){
System.out.println(field);
}
//获取本类及其父类的所有的属性
//Student extends Person extends Object
for (Class<?> c = clazz;c!=null;c=c.getSuperclass()){
Field[] fields1 = c.getDeclaredFields();
for (Field field:fields1){
System.out.println(field);
}
}
try {
//获取本类指定的属性对象
Field field = clazz.getDeclaredField("classId");
System.out.println(field);
//获取属性参数值
int modifiers = field.getModifiers();
System.out.println("是否使用Public修饰:"+ Modifier.isPublic(modifiers));
System.out.println("是否使用Static修饰:"+ Modifier.isStatic(modifiers));
System.out.println("是否使用Final修饰:"+ Modifier.isFinal(modifiers));
System.out.println("是否使用Private修饰:"+ Modifier.isPrivate(modifiers));
System.out.println("是否使用Protected修饰:"+ Modifier.isProtected(modifiers));
System.out.println("是否使用Transient修饰:"+ Modifier.isTransient(modifiers));
} catch (NoSuchFieldException e) {//属性名没有匹配到对应的属性就报该异常
throw new RuntimeException(e);
}
//通过反射设置对象里的属性
Student stus = new Student("xxx","x",23,"2401","001");
Field field = clazz.getDeclaredField("id");
field.setAccessible(true);
field.set(stus,"666");
System.out.println(stus);
//通过反射设置对象里的属性
Student stu = new Student();
ReflexUtil.setField(stu,"name","xxx");
ReflexUtil.setField(stu,"sex","xxx");
ReflexUtil.setField(stu,"age",21);
ReflexUtil.setField(stu,"classId","001");
ReflexUtil.setField(stu,"id","0001");
System.out.println(stu);
}
}
5)反射-操作方法
/**
* 创建对象
* @param clazz class对象
* @param paremeters 构造方法中的参数数据
* @return 对象
* @param <T>
*/
public static <T> T newInstance(Class<T> clazz,Class<?>[] paremeterTypes,Object[] paremeters){
try {
Constructor<T> constructor = clazz.getDeclaredConstructor(paremeterTypes);
constructor.setAccessible(true);
T obj = constructor.newInstance(paremeters);
return obj;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* 知识点:利用反射操作构造方法
*/
public class Test04 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
//1.拿到对象
Properties p = new Properties();
p.load(Test04.class.getClassLoader().getResourceAsStream("classPath.properties"));
String path = p.getProperty("path");
Class<?> clazz = Class.forName(path);
//获取该类公有的构造方法对象
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor:constructors){
System.out.println(constructor);
}
//获取该类所有的构造方法对象
Constructor<?>[] constructorss = clazz.getDeclaredConstructors();
for (Constructor<?> constructor:constructorss){
System.out.println(constructor);
}
//获取无参构造对象
Constructor<?> constructor = clazz.getDeclaredConstructor();
//利用无参构造对象创建该类的对象
constructor.setAccessible(true); //设置修改权限
Student stu = (Student)constructor.newInstance();
System.out.println(stu);
//利用无参构造对象创建该类的对象 --简化版
Student stus = (Student)constructor.newInstance();
System.out.println(stus);
//利用有参构造对象
Constructor<?> constructor1 = clazz.getDeclaredConstructor(String.class, String.class, int.class, String.class, String.class);
//利用有参构造对象创建该类的对象
constructor1.setAccessible(true); //设置修改权限
Student student = (Student) constructor1.newInstance("xxx","x",21,"1001","001");
System.out.println(student);
//利用反射工具去创建对象 -- 底层调用无参构造
Student student1 = ReflexUtil.newInstance(Student.class,null,null);
System.out.println(student1);
//利用反射工具去创建对象 -- 底层调用有参构造
Class<?>[] parameterTypes = {String.class,String.class,int.class,String.class,String.class};
Object[] parameters = {"xxx","x",20,"1001","001"};
Student student2 = ReflexUtil.newInstance(Student.class,parameterTypes,parameters);
System.out.println(student2);
}
}
6)反射-操作数组
7)反射-操作泛型
8)反射的实际应用
41.JDK1.8新特性
JDK1.8新特性简介
- 速度更快 - 优化底层源码,比如HashMap,ConcurrentHashMap
- 代码更少 - 添加新的语法Lambda表达式
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常
Lambda表达式
简介
Lambda是一个匿名函数(方法),允许把函数作为一个方法的参数,利用Lambda表达式可以写出更简洁、更灵活的代码,作为一种紧凑的代码风格,使java得语言表达能力得到了提升
一般都是优化匿名内部类
注意:
- 重写方法得形参只有一个时,可以不加小括号
- Lambda表达式中不允许声明一个与局部变量同名的参数或局部变量
- Lambda表达式中访问外层的局部变量,外层的局部变量自动变为隐式常量,默认添加final修饰
- 重写方法的形参同时加类型或同时不加类型修饰
函数式接口:
简介
函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionallInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错,配合Lambda表达式一起使用
四大核心函数式接口
方法、构造方法和数组的引用
方法、构造方法和数组引用就是Lambda表达式的另一种表现形式
方法引用
若Lambda表达式中的内容由方法已经实现了,可以使用方法引用这个技能
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面
对象::实例方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
类名::静态方法
构造方法引用
数组引用
Stream
简介
Stream(流)是数据渠道,用于操作数据源(集合,数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式。
注意:
- Stream不会存储数据
- Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
- Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
执行步骤
1.创建Stream:通过数据源(集合、数组等)获取一个Stream
2.中间操作:中间操作链,对源数据的数据进行处理
3.终止操作:执行中间操作,并产生结果
注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载。
6)反射-操作数组
7)反射-操作泛型
8)反射的实际应用
41.JDK1.8新特性
JDK1.8新特性简介
- 速度更快 - 优化底层源码,比如HashMap,ConcurrentHashMap
- 代码更少 - 添加新的语法Lambda表达式
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常
Lambda表达式
简介
Lambda是一个匿名函数(方法),允许把函数作为一个方法的参数,利用Lambda表达式可以写出更简洁、更灵活的代码,作为一种紧凑的代码风格,使java得语言表达能力得到了提升
一般都是优化匿名内部类
注意:
- 重写方法得形参只有一个时,可以不加小括号
- Lambda表达式中不允许声明一个与局部变量同名的参数或局部变量
- Lambda表达式中访问外层的局部变量,外层的局部变量自动变为隐式常量,默认添加final修饰
- 重写方法的形参同时加类型或同时不加类型修饰
函数式接口:
简介
函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionallInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错,配合Lambda表达式一起使用
四大核心函数式接口
[外链图片转存中…(img-jl3DiyCx-1716433498203)]
[外链图片转存中…(img-re3rvp8O-1716433498204)]
方法、构造方法和数组的引用
方法、构造方法和数组引用就是Lambda表达式的另一种表现形式
方法引用
若Lambda表达式中的内容由方法已经实现了,可以使用方法引用这个技能
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面
对象::实例方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
类名::静态方法
构造方法引用
数组引用
Stream
简介
Stream(流)是数据渠道,用于操作数据源(集合,数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式。
注意:
- Stream不会存储数据
- Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
- Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
执行步骤
1.创建Stream:通过数据源(集合、数组等)获取一个Stream
2.中间操作:中间操作链,对源数据的数据进行处理
3.终止操作:执行中间操作,并产生结果
注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载。