常用的API
一、Math工具类的常用方法
public static int abs(int a)// 获取参数绝对值 public static double ceil(double)// 向上取整 //bug 以int类型为例,取值范围:-2147483648~2147483647 //-2147483648没有正数与负数对应,那么abs结果产生 public static double floor(double)//向下取整 public static int round(float a)//四舍五入 public static int max(int a,int b)//获取两个int值中的较大值 public static double pow(double a,double b)//返回a的b次幂的值 public static double random()//返回值为double的随机值,范围[0.0,1.0)
二、System工具类
public static void exit(int status)// 终止当前运行的java虚拟机。 //0表示正常停止运行.非0表示不正常停止异常。 public static long currentTimeMillis()//返回当前系统的时间毫秒值形式。 //可以记录当时间与java时间原点的时间间隔,可以用来计算程序运行时间。 //时间原点:1970年1月1日0:0:0,我国在东八区,有8个小时时差。 1s = 1000ms public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数)// 数组拷贝 int[] arr1={1,2,3,4,5,6,7,8,9,10}; int[] arr2=new int[10]; System.arraycopy(arr1,0,arr2,10); //实例:拷贝成如下情况: //0 0 0 0 1 2 3 0 0 0 System.arraycopy(arr1,0,arr2,4,3); //0 0 7 8 9 0 0 0 0 0 System.arraycopy(arr1,6,arr2,2,3); //如果数据源数组和目的地的数组都是基本数据类型,那么两者必须保持一致。否则报错。 //考虑的时候需要考虑数组的长度,如果超出范围也会报错。 //如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型。
三、Runtime
//Runtime不能直接new生成对象,只能调用方法 public static Runtime getRuntime()//当前系统虚拟机的运行环境对象 public void exit(int status) //停止虚拟机 public int availableProcessors()//获取CPU的线程数 public long maxMemory()//JVM能从系统中获取总内存大小(单位byte) public long totalMemory()//JVM已经从系统中获取总内存大小(单位byte) public long freeMemory() //JVM剩余内存大小(单位byte) public Process exec(String command)//运行cmd命令 //1.获取Runtime对象 Runtime r1 = Runtime.getRuntime(); //2.exit 停止虚拟机 Runtime.getRuntime().exit(0); //3.获取CPU的线程数 System.out.println(Runtime.getRuntime().availableProcessors()); //4.总内存大小,单位byte字节 System.out.println(Runtime.getRuntime().maxMemory()/1024/1024)//4064以M为单位 //5.已经获取的总内存大小,单位byte字节 System.out.println(Runtime.getRuntime().totalMemory()/1024/1024); //6.剩余内存大小 System.out.println(Runtime.getRuntime().freeMemory()/1024/1024) //7.运行cmd命令 Runtime.getRuntime().exec("dir"); /* shutdown:关机 加上下面的参数才能执行 "-s" :默认在1分钟之后关机 "-s -t" : 指定时间:指定关机时间 "-a" : 取消关机操作 "-r" :关机并重启 */ System.out.println(str2); //System:类名 //out:静态变量 //System.out:获取打印的对象 //println():方法 //参数:表示打印的内容 //核心逻辑: //当我们打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串。然后再打印在控制台上打印完毕换行处理。
四、Object和Objects类
//Object 是Java中的顶级父类,所有的类都直接或间接的继承于Object类 //Object 类中的方法可以被所有子类访问,所以我们要学习Object类和其中的方法。 //Object只有空参构造 public Object()//空参构造 public String toString()//返回对象的字符串表示形式 public boolean equals(Object obj)//比较两个对象是否相等 protected Objecct clone(int a)//对象克隆 /*********************************************************/ System.out.println(str2); //System:类名 //out:静态变量 //System.out:获取打印的对象 //println():方法 //参数:表示打印的内容 //核心逻辑: //当打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串。然后再打印在控制台上打印完毕换行处理。 //思考:默认情况下,Object类中的toString方法返回的是地址值 //所以,默认情况下,打印一个对象打印的就是地址值。 //要想看到内容部属性我们需要在类中进行重写Object类中的toString方法,在重写的时候把对象的属性值进行字符串拼接。 public String toString() return "Student{name=}"+name+",age="+age+"}"; /*********************************************************/ public boolean equals(Object obj)//比较两个对象是否相等 //1.如果没有重写equals方法,那么默认使用Object中的方法进行比较,比较的是地址值是否相等。 //2.一般来讲地址值对于我们来说意义不大,所以我们会重写,重写之后比较的就是对象内部的属性值了。 public boolean equals(Object o){ if(this==o)return true; if(o==null||getClass()!=o.getClass())return false; Student student = (Student)o; return age == student.age&&Object.equals(name,student.name); } String s="abc"; StringBulider sb= new StringBuilder("abc"); System.out.println(s.equals(sb));//false //因为equals方法是被s调用的,而s是字符串 //所以equals要看String类中的字符串中的equals方法,先判断参数是否为字符串,再比较内部的属性,但是如果参数不是字符串,直接返回false System.out.println(sb.equals(s)); //因为equals方法是被sb调用的,而sb是StringBuilder的对象,所以这里的equals方法要看StringBuilder中的equals方法,那么在StringBuilder当中,没有重写equals方法,使用是Object中的equals方法,在Object当中默认是使用==号比较两个对象的地址值而这里的s和sb记录的地址值是不一样的,所以结果返回false /********************************************************/ protected Objecct clone(int a)//对象克隆 //1.先创建一个对象 int[] data={1,2,3,4,5,6,7,8,9,10}; User u1 = new User(1,"zhangsan","1234qwer","girl11",data); //2.克隆对象 //细节: //方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。 protected Object clone() throws CloneNotSupportedException{ //调用父类中的clone方法 //相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去。 return super.clone(); } //书写细节 //1.重写Object中clone方法 //2.让Javabean类实现Cloneable接口 //3.创建原对象并调用clone就可以了。 User u2=(User)u1.clone(); //&&浅克隆:不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来。Object中的克隆是浅克隆 //&&深克隆:基本数据类型拷贝过来,字符串复用引用数据类型会重新创建新的 //Object要想进行深克隆需要进行克隆重写 protected Object clone() throws CloneNotSupportedException{ //先把被克隆对象中的数组获取出来 int[] data = this.data; //创建新的数组 int[] newData = new int[data.length]; //拷贝数组中的数据 for(int i=0;i<data.length;i++){ newData[i]=data[i]; } //调用父类中的方法克隆对象 User u = (User)super.clone(); //因为父类中的克隆方法是浅克隆,替换克隆出来对象中的数组地址值 } /*&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&& Object是Java中的顶级父类。 所有的类都直接或间接的继承Object类 toString():一般会重写,打印对象时打印属性 equals():比较对象时会重写,比较对象属性值是否相同 clone():默认浅克隆。 如果需要深克隆需要重写方法或者使用第三方工具类。 */
// Objects是一个工具类,提供了一些方法去完成一些功能。 //Object 的成员方法 public static boolean equals(Object a,Object b)//先做非空判断,比较两个对象 public static boolean isNull(Object obj)//判断对象是否为null,为null返回true,反之 public static boolean nonNull(Object obj)//判断对象是否为null,跟isNull的结果相反
五、BigInteger和BigDecimal
/*BigIntrger 在Java中,整数有4种类型:byte(1 byte),short(2 byte),int(4 byte),long(8 byte) */ public BigInteger(int num,Random rnd)//获取随机大整数,范围:[0~2的num次方-1]; public BigInteger(String val)//获取指定的大整数。 public BigInteger(String val,int radix)//获取指定进制的大整数 public static BigInteger valueOf(long val)//静态方法获取BigInteger的对象,内部有优化 //对象一旦被创建内部记录的值就不能发生改变 /************************************************/ //1.获取一个随机的大整数 Random r = new Random(); BigInteger bd1 = new BigInteger(4,r); //获得一个0 ~ 2^4-1 (15)直接的随机数 //2.获取一个指定的大整数 //细节:字符串中必须是整数,否则会报错、 BigInteger bd1=new BigInteger("10000"); //3.获取指定进制的大整数 //细节: //1.字符串中的数组必须是整数 //2.字符串中的数字必须要跟进制吻合。 BigInteger bd4 = new BigInteger("123",10); //输出结果为123; //4.静态方法获取BigInteger的对象,内部有优化 //细节: //1.能够显示的范围比较小,在long的取值范围之内,超出long会报错。 //2.在内部对常用的数字:-16~16进行了优化。 //提前把-16~16创建好BigInteger的对象,如果多次使用不会创建新的对象。可以用==号判断地址 BigInteger bd5=BigInteger.valueOf(100); //此时的bd5的大小为100; /*BigInteger构造方法小结 1.如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。 2.如果BigInteger表示的超出long的取值范围,可以用构造方法获取。 3.对象一旦创建,BigInteger内部记录的值不能发生改变。 4.只要进行计算都会产生一个新的BigInteger对象。 */ /*****************************************************/ //BigInteger常见成员方法 public BigInteger add(BigInteger val)// 加法 public BigInteger subtract(BigInteger val)// 减法 public BigInteger multiply(BigInteger val)// 乘法 public BigInteger divide(BigInteger val)//除法,获取商 public BigInteger[] divideAndRemainder(BigInteger val)//除法,获取商和余数 public boolean equals(Object x)//比较是否相同 public BigInteger pow(int exponent)//次幂 public BigInteger max/min(BigInteger val)//返回较大值/较小值 public BigInteger intValue(BigInteger val)//转为int类型整数,超出范围数据有误。 /*********************************************************/ //BigInteger底层存储方式 将大数的数字分割成Long型数字存储到数组之中。 /* 总结: 1.BigInteger表示一个大整数。 2.如何获取BigInteger的对象? BigInteger b1 = BigInteger.valueOf(0.1); BigInteger b1 = new BigInteger("整数"); 3.常见操作 加:add 减:subtract 乘:multiply 除:divide、divideAndRemainder 比较:equals、max、min 次幂:pow 转成整数:intValue、longValue */
// BigDecima的作用 // 1.用于小数的精确计算 // 2.用来表示很大的小数 /* 构造方法获取BigDecimal对象 public BigDecimal(double val) public BigDecimal(String val) 静态方法获取BigDecimal对象 public static BigDecimal valueOf(double val) 1.通过传递double类型的小数来创建对象 细节: 这种方式有可能是不精确的,所以不建议使用 */ BigDecimal bd1 = new Bigdecimal(0.01); BigDecimal bd2 = new Bigdecimal(0.09); //精度会丢失。 // 2.通过传递字符串表示的小数来创建对象 BigDecimal bd3 = new BigDecimal("0.01"); BigDecimal bd4 = new BigDecimal("0.09"); BigDecimal bd5 = bd3.add(bd4); //3.通过静态方法获取对象 BigDecimal bd6 = BigDecimal.valueOf(10); //细节: //如果表示的数字没有超过double的范围使用静态 //否则使用构造方法,如果我们传递的是0~10之间的数那么会返回已经创建好的对象,不会创建新对象。 //BigDecimal的使用方法 public BigDecimal add(BigDecimal val)// 加法 public BigDecimal subtract(BigDecimal val)// 减法 public BigDecimal multiply(BigDecimal val)// 乘法 public BigDecimal divide(BigDecimal val)//除法 //除不尽的数用下面这个方法,上面只能用能除尽的数。 public BigDecimal divide(BigDecimal val,精确几位,舍入模式)//除法 BigDecimal bd6 =bd1.divide(bd2,2,BigDecimal.RoundingMode.HALF_UP);//保留两位小数,向上取整 //BigDecimal的存储方法将每个字符存储到数组当中 /* BigDecimal的作用是什么? 表示较大的小数和解决小数运算精度丢失真问题 BigDecimal的对象如何获取? BigDecimal db1 = newBigDecimal("较大的数"); BigDecimal db2 = BigDecimal.valueOf(0.1); 常见操作: 加:add 减:subtract 乘:multiply 除:divide(四舍五入:RoundingMode.HALF_UP) */
六、时间类
1.Date存储时间数据类
//java语言规定,1970年1月1日0时0分0秒认为是时间源点。 //Date类的构造方法 public Date() //创建一个Date对象,代表的是系统当前此刻日期时间。 public Date(long time)//把时间毫秒值转换成Date日期对象。 //常见的方法 public long getTime()//返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 Date date = new Date(); System.out.println(date); System.out.println(date.getTime());//获得距离时间源点的时间以毫秒为单位 System.out.println(date.getTime()/(60*60*24));//获得时间源点距离现在的天数 /*********************************************************/ public void setTime(long time)//设置日期对象的时间为当前时间毫秒值对应的时间 date.setTime(1000*60*60);//中东地区比源点时间多出8个小时 Thu Jan 01 09:00:00 CST 1970 System.out.println(date);
2.SimpleDateFormat控制时间格式类
//SimpleDateFormat构造器 public SimpleDateFormat(Stirng pattern)//创建简单日期格式化对象,并封装时间的格式。 //SimplpDateFormat时间的方法 public final String format(Date date)//将日期格式化成日期/时间字符串 public final String format(Object time)//将时间毫秒值式化成日期/时间字符串 public final Data parse(String val)//将指定封装格式的日期转换为毫秒值形式。
字母 表示含义 yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒 SSS 毫秒 "2022年12月12日" 的格式是 "yyyy年MM月dd日" "2022-12-12 12:12:12" 的格式是 "yyyy-MM-dd HH:mm:ss" 按照上面的格式可以任意拼接,但是字母不能写错
3.Calendar日历类
//calendar日历类的常见方法 public static Calendar getInstance()//获取当前日历对象 public int get(int field)// 获取日历中的某个信息 public final Date getTime()//获取日期对象 public long getTimeInMillis()//获取时间毫秒值 public void set(int field,int value)//修改日历的某个信息。 public void add(int field,int amount)//为某个信息增加/减少指定的值。
public class Test4Calendar { public static void main(String[] args) { // 目标:掌握Calendar的使用和特点。 // 1、得到系统此刻时间对应的日历对象。 Calendar now = Calendar.getInstance(); System.out.println(now); // 2、获取日历中的某个信息 int year = now.get(Calendar.YEAR); System.out.println(year); int days = now.get(Calendar.DAY_OF_YEAR); System.out.println(days); // 3、拿到日历中记录的日期对象。 Date d = now.getTime(); System.out.println(d); // 4、拿到时间毫秒值 long time = now.getTimeInMillis(); System.out.println(time); // 5、修改日历中的某个信息 now.set(Calendar.MONTH, 9); // 修改月份成为10月份。 now.set(Calendar.DAY_OF_YEAR, 125); // 修改成一年中的第125天。 System.out.println(now); // 6、为某个信息增加或者减少多少 now.add(Calendar.DAY_OF_YEAR, 100); now.add(Calendar.DAY_OF_YEAR, -10); now.add(Calendar.DAY_OF_MONTH, 6); now.add(Calendar.HOUR, 12); now.set(2026, 11, 22); System.out.println(now); } }
4.JDK8
新增日期类
传统的时间类
1.设计不合理,使用不方便,很多都被淘汰。
2.很多都是可变对象,修改后会丢失最开始的时间信息。
3.线程不安全。
4.不能精确到纳秒,只能精确到毫秒。
JDK8开始之后新增的时间API
1,都是不可变对象,修改后会返回新的时间对象,不会丢失最开始的时间。
2.线程更安全
3.能精确到毫秒、纳秒。
//LocalDate类的基本使用 LocalDate ld = LocalDate.now();//被私有化只能通过构造方法获取对象。 //获取时间信息 System.out.println(ld);//获取 年 月 日 public int getYear()//年 public int getMonthValue()//月 public int getDayOfMonth()//日 public int getDayOfYear()//一年里的第几天 public DayOfWeek getDayOfWeek()//星期几(英语) //getDayOfWeek().getValue()//得到相应的数字 public int getHour()//时 public int getMinute()//分 public int getSecond()//秒 public int getNano()//纳秒 /***********************修改时间信息***********************/ public LocalDateTime withYear()// 年 public LocalDateTime withMonth()// 月 public LocalDateTime withDayOfMonth()//月中的第几日 public LocalDateTime withDayOfYear()//年中的第几日 //LocalDate只包含上面几个方法 public LocalDateTime withHour()//小时 public LocalDateTime withMinute()//分钟 public LocalDateTime withSecond()//秒 public LocalDateTime withNano()//纳秒 /********************添加相应的时间*****************************/ // plusYears plusMonths plusDays plusWeeks plusHours plusMinutes plusSeconds plusNanos LocalDateTime ldt4 = ldt.plusYears(2); LocalDateTime ldt5 = ldt.plusMinutes(3); /********************减少相应的时间**************************/ // minusDays minusYears minusMonths minusWeeks minusHours minusMinutes minusSeconds minusNanos LocalDateTime ldt6 = ldt.minusYears(2); LocalDateTime ldt7 = ldt.minusMinutes(3); //&& 5、获取指定日期和时间的LocalDateTime对象: // public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour,int minute, int second, int nanoOfSecond) LocalDateTime ldt8 = LocalDateTime.of(2029, 12, 12, 12, 12, 12, 1222); LocalDateTime ldt9 = LocalDateTime.of(2029, 12, 12, 12, 12, 12, 1222); //&& 6、 判断2个日期、时间对象,是否相等,在前还是在后: equals、isBefore、isAfter System.out.println(ldt9.equals(ldt8)); System.out.println(ldt9.isAfter(ldt)); System.out.println(ldt9.isBefore(ldt)); //&& 7、可以把LocalDateTime转换成LocalDate和LocalTime // public LocalDate toLocalDate() // public LocalTime toLocalTime() // public static LocalDateTime of(LocalDate date, LocalTime time) LocalDate ld = ldt.toLocalDate(); LocalTime lt = ldt.toLocalTime(); LocalDateTime ldt10 = LocalDateTime.of(ld, lt);
时区ZoneID和ZonedDateTime
//ZonedDateTime和ZoneId的构造方法都被私有化。 /******************1.ZoneId的常见方法:**************/ //public static ZoneId systemDefault():获取系统默认的时区 ZoneId zoneId = ZoneId.systemDefault(); // public static Set<String> getAvailableZoneIds(): 获取Java支持的全部时区Id System.out.println(ZoneId.getAvailableZoneIds()); // public static ZoneId of(String zoneId) : 把某个时区id封装成ZoneId对象。 ZoneId zoneId1 = ZoneId.of("America/New_York"); /*****************2、ZonedDateTime:带时区的时间。*******************/ // public static ZonedDateTime now(ZoneId zone): 获取某个时区的ZonedDateTime对象。 ZonedDateTime now = ZonedDateTime.now(zoneId1); // 世界标准时间了 ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC()); // public static ZonedDateTime now():获取系统默认时区的ZonedDateTime对象 ZonedDateTime now2 = ZonedDateTime.now();
Instant类
//对象已经被私有化只能通过方法进行获取 public static Instant now()//获取当前时间的Instant对象(标准时间) public long getEpochSecond()//获取从1970-01-01T00:00:00开始记录的秒数。 public int getNano()//从时间线开始,获取从第二个开始的纳秒数 plusMillis plusSeconds plusNanos//增加时间系列的方法 minusMillis minusSeconds minusNanos//减少时间系列的方法。 equals、isBefore、isAfter//判断时间系列的方法 /***********************************************/ Instant now = Instant.now(); // 创建对象获取此刻时间信息 // 2、获取总秒数 long second = now.getEpochSecond(); // 3、不够1秒的纳秒数 int nano = now.getNano(); // Instant对象的作用:做代码的性能分析,或者记录用户的操作时间点 Instant now1 = Instant.now(); /* 代码区域 */ Instant now2 = Instant.now(); LocalDateTime l = LocalDateTime.now();
DateTimeFormatter格式化器
//DateTimeFormatter public static DateTimeFormatter ofPattern(时间格式)//获取格式化器对象 public String format(时间对象)//格式化时间 //LocalDateTime提供的格式化、解析时间的方法。 public String format(DateTimeFormatter formatter)//格式化时间 public static LocalDateTime parse(CharSequence text,DateTimeFormatter formatter)//解析时间
//代码演示格式化器的用法。 // 1、创建一个日期时间格式化器对象出来。 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); // 2、对时间进行格式化 LocalDateTime now = LocalDateTime.now(); String rs = formatter.format(now); // 正向格式化 String rs2 = now.format(formatter); // 反向格式化 // 4、解析时间:解析时间一般使用LocalDateTime提供的解析方法来解析。 String dateStr = "2029年12月12日 12:12:11"; LocalDateTime ldt = LocalDateTime.parse(dateStr, formatter);
Period类和Duration类
//这两个类可以用来对计算两个时间点的时间间隔。 //Period用来计算日期间隔(年、月、日) //Duration用来计算时间间隔(时、分、秒、纳秒) public static Period betWeen(LocalDate start,LocalDate end)//传入2个日期对象,得到Period对象 public int getYears()//计算隔几年,并返回 public int getMonths()//计算隔几个月并返回 public int getYears()//计算隔多少天,并返回 //实例代码如下: LocalDate start = LocalDate.of(2029, 8, 10); LocalDate end = LocalDate.of(2029, 12, 15); // 1、创建Period对象,封装两个日期对象。 Period period = Period.between(start, end); // 2、通过period对象获取两个日期对象相差的信息。 System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); /***********************************************************************/ //Duration可以用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数;支持LocalTime、LocalDateTime、Instant等时间 //方法如下 public static Duration between(开始时间对象1,截止时间对象2)//传入2个时间对象,得到Duration对象 public long toDays()//计算隔多少天,并返回 public long toHouts()//计算隔多少小时,并返回 public long toMinutes()//计算隔多少分,并返回 public long toSeconds()//计算隔多少秒,并返回 public long toMillis()//计算隔多少毫秒,并返回 public long toNanos()//计算隔多少纳秒,并返回 /**********************实例代码*************************/ LocalDateTime start = LocalDateTime.of(2025, 11, 11, 11, 10, 10); LocalDateTime end = LocalDateTime.of(2025, 11, 11, 11, 11, 11); // 1、得到Duration对象 Duration duration = Duration.between(start, end); // 2、获取两个时间对象间隔的信息 System.out.println(duration.toDays());// 间隔多少天 System.out.println(duration.toHours());// 间隔多少小时 System.out.println(duration.toMinutes());// 间隔多少分 System.out.println(duration.toSeconds());// 间隔多少秒 System.out.println(duration.toMillis());// 间隔多少毫秒 System.out.println(duration.toNanos());// 间隔多少纳秒
七、Arrays类
1、Arrays的基本使用
//Arrays是操作数组的工具类 //基本使用方法:遍历、拷贝、排序等操作 //1、public static String toString(类型[] arr): 返回数组的内容 int[] arr = {10, 20, 30, 40, 50, 60}; System.out.println(Arrays.toString(arr)); //2、public static 类型[] copyOfRange(类型[] arr,起始索引,结束索引):拷贝数组(指定范围,包前不包后) int[] arr2 = Arrays.copyOfRange(arr, 1, 4); //将arr数组下标为1到下标为4-1=3的数组元素复制到arr2中 System.out.println(Arrays.toString(arr2)); //3、public static copyOf(类型[] arr, int newLength):拷贝数组,可以指定新数组的长度。 int[] arr3 = Arrays.copyOf(arr, 10); //4、public static setAll(double[] array, IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。 Arrays.setAll(prices, new IntToDoubleFunction() { @Override public double applyAsDouble(int value) {//value表示数组索引。 return prices[value]*2;//将数组中的数据扩大两倍。 } }); // 5、public static void sort(类型[] arr):对数组进行排序(默认是升序排序) Arrays.sort(arr); Arrays.sort(arr,0,7); System.out.println(Arrays.toString(arr)); //
2、Arrays操作对象数组
//按照自己的方式进行操作对象 //让Student类实现Comparable接口,同时重写compareTo方法。Arrays的sort方法底层会根据compareTo方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。 /****************Comparable泛型接口****************/ public interface Comparable <E>{ public int compareTo(E e); } /*************************************************/ @Override public int compareTo(Student o) { // 约定1:认为左边对象 大于 右边对象 请您返回正整数 // 约定2:认为左边对象 小于 右边对象 请您返回负整数 // 约定3:认为左边对象 等于 右边对象 请您一定返回0 /*if(this.age > o.age){ return 1; }else if(this.age < o.age){ return -1; } return 0;*/ //上面的if语句,也可以简化为下面的一行代码 return this.age - o.age;// 按照年龄升序排列 // return o.age - this.age;// 按照年龄降序排列 } /**************************************************/ /* 在调用Arrays.sort(数组,Comparator比较器);时,除了传递数组之外,传递一个Comparator比较器对象。Arrays的sort方法底层会根据Comparator比较器对象的compare方法方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。 */ // 2、public static <T> void sort(T[] arr,Comparator<泛型> c) // 参数一:需要排序的数组 // 参数二:Comparator比较器对象(用来制定对象的比较规则) Arrays.sort(students, new Comparator<Student>() { // 制定比较规则了:左边对象 o1 右边对象 o2 // 约定1:认为左边对象 大于 右边对象 请您返回正整数 // 约定2:认为左边对象 小于 右边对象 请您返回负整数 // 约定3:认为左边对象 等于 右边对象 请您一定返回0 @Override public int compare(Student o1, Student o2) { //return Double.compare(o1.getHeight(),o2.getHeight());//升序 return o1.getAge()-o2.getAge();//升序 //return o2.getAge()-o1.getAge();//降序 } });
八、Lambda表达式
1.Lambda基本使用
//作用:用于简化函数式接口的匿名内部类代码 //使用Lambda表达式前提必须是仅有一个抽象方法的接口又称之为函数式接口。 Swimming s = new Swimming() { //匿名内部类普通写法 @Override public void swim() { System.out.println("xxx在快乐游泳~~~~~~~~~~~~"); } }; s.swim() /******************************************/ Swimming s1=()->{ //使用Lambda表达式对Swimming接口的匿名内部类进行简化 System.out.println("yyy在快乐游泳~~~~~~~~~~~~"); }; s1.swim();
2.Lambda表达式省略规则
// 目标:使用Lambda简化函数式接口。 double[] prices = {99.8, 128, 100}; /*****************************************/ 1.Lambda的标准格式 (参数类型1 参数名1, 参数类型2 参数名2)->{ ...方法体的代码... return 返回值; } //把所有元素*0.8: 改用Lamdba表达式写法 Arrays.setAll(prices, (int value) -> { return prices[value] * 0.8; }); /******************************************/ 2.在标准格式的基础上()中的参数类型可以直接省略 (参数名1, 参数名2)->{ ...方法体的代码... return 返回值; } Arrays.sort(students, ( o1, o2) -> { return Double.compare(o1.getHeight(), o2.getHeight()); // 升序 }); /*****************************************/ 3.如果{}总的语句只有一条语句,则{}可以省略、 return关键字、以及最后的“;”都可以省略 (参数名1, 参数名2)-> 结果 Arrays.sort(students, ( o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())); /****************************************/ 4.如果()里面只有一个参数,则()可以省略 (参数名)->结果 Arrays.setAll(prices, value-> prices[value] * 0.8);
九、JDK8新特性
1.静态方法的引用
//目的进一步简化Lambda表达式的 //方法引用的标志性符号为“::” //如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用。 public static int comerAge(Student s,Student s2){ return s.getAge()- s2.getAge(); } //静态方法 Arrays.sort(students, new Comparator<Student>() { @Override//匿名内部类的构造形式 public int compare(Student o1, Student o2) { return o1.getAge()- o2.getAge(); } }); Arrays.sort(students,(o1,o2)->o1.getAge()-o2.getAge());//Lambda表达式 Arrays.sort(students,(o1,o2)->Compers.comerAge(o1,o2));//Lambda表达式引用静态方法 Arrays.sort(students,Compers::comerAge);//最简形式
2.实例方法的引用
//实例方法的引用 //对象名::实例方法 //使用场景 //如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。 public class Compers { public static int comerAge(Student s,Student s2){ return s.getAge()- s2.getAge(); } public int compareByAgeDesc(Student s,Student s2){ return s2.getAge()-s.getAge(); } } Arrays.sort(students,(o1,o2)->(new Compers()).compareByAgeDesc(o1,o2)); Arrays.sort(students,new Compers()::compareByAgeDesc);
3.特定类型的方法引用
//类型::方法 //使用场景 // 如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。 String[] a = {"sdf","ddea","tda","dtg","tgj","you","ddg"}; /* Arrays.sort(a, new Comparator<String>() { //构造匿名内部类 @Override public int compare(String o1, String o2) { return o1.compareToIgnoreCase(o2); } }); */ // Arrays.sort(a,(o1,o2)->o1.compareToIgnoreCase(o2)); Arrays.sort(a, String::compareToIgnoreCase); System.out.println(Arrays.toString(a).toString());
4.构造器引用
//类名::new。 /*使用场景 如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。 */ CreatCar c = new CreatCar() { @Override//构造匿名内部类 public Car creat(String name, double price) { return new Car(name,price); } }; CreatCar c =(name,price)->new Car(name,price);//Lambda表达式的结果 CreatCar c = Car::new;//构造器引用的最终形式。
十、简单算法
1.什么是算法?
算法是解决某个实际问题的过程方法!!
2.冒泡排序特点是:
依次将相邻两个数字进行比较进行交换位置
3.选择性排序的特点是:
依次固定一个位置从其他位置选择需要的数据进行交换
十一、正则表达式
// 正则表达式的作用: /* 1.检验字符串是否满足规则。 2.在一段文本中查找满足要求的内容。 /******************字符类(只匹配一个字符)*********************/ [abc]: 只能是a,b,c [^abc]: 除了a,b,c之外的任何字符 [a-zA-Z]: a到zA到Z包括(范围) [a-d[m-p]]: a到d,或m到p [a-z&&[def]]: a-z和def的交集。为:d,e,f; [a-z&&[^bc]]: a-z和非bc的交集。(等同于[ad-z]) [a-z&&[^m-p]]: a到z和除了m到p的交集。(等同于[a-[q-z]]) /***********************************************************/ // 如果要求两个范围的交集,那么必须写&&两个符号 System.out.println("n".matches("[a-zA-Z]"));ture /* 一次只能判断一个字符。 */ /*****************预定义字符(只匹配一个字符)******************/ . : 任何字符 \d 一个数字:[0-9] \D 非数字:[^0-9] \s 一个空白字符:[\t\n\x0B\f\r] \S 非空白字符:[^\s] \w [a-zA-Z_0-9]英文、数字、下划线 \W [^\W]一个非单词字符 */ System.out.println("3".matches("\\d"));//ture /*********************数量词*******************************/ X? X,一次或0次 X* X,零次或多次 X+ X,一次或多次 X{n} X,正好n次 X{n,} X,至少n次 X{n,m} X,至少n但不超过m次 System.out.println("3".matches("\\d{0?}"));//ture System.out.println("23df".matches("[\\w&&[^_]]{4}"));//true /*******************************************************/
十二、异常
1.认识异常
//异常的体系 // Java.lang.Throwable 包含Error 和 Exception /* Error:代表的系统级别错误(属于严重问题),也就是说系统一旦出现问题,sun公司会把这些问题封装成Error对象给出来。Error一般不是程序员使用,而是sun公司自己使用。 Exception:叫异常,它代表的才是我们程序可能出现的问题,所以,我们程序员通常会用Exception以及它的子类来封装。 &&运行时异常:RuntimeException及其子类,编译阶段不会出现错误提醒,运行是出现的异常(如:数组索引越界异常)。 &&编译时异常:编译阶段就会出现错误提醒的。(如:日期异常) */ int[] arr = {11,22,33}; //5是一个不存在的索引,所以此时产生ArrayIndexOutOfBoundsExcpetion System.out.println(arr[5]); /** * 目标:认识异常。 第一种:使用throws在方法上声明,意思就是告诉下一个调用者,这里面可能有异常 */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse("2028-11-11 10:24"); System.out.println(d); //第二种:使用try...catch语句块异常进行处理。 try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse("2028-11-11 10:24"); System.out.println(d); } catch (ParseException e) { e.printStackTrace(); }
2.自定义异常
/*自定义运行时异常 定义一个异常类继承RuntimeException 重写构造器 通过throw new 异常类(XXX)来创建异常对象并抛出。 提醒不强烈,运行时才可能出现 */ /*自定义编译时异常 定义一个异常类继承Exception 重写构造器 通过throw new 异常类(XXX)来创建异常对象并抛出。 编译阶段就会报错,提醒更加强烈。 */ /***********************运行时异常**********************/ public class ArrayIsEmptyException extends RuntimeException{ public ArrayIsEmptyException() { } public ArrayIsEmptyException(String message) { super(message); } } /****************************************************/ public static void main(String[] args) { int[] arr={2,4,1,4,7,4,24,7,8,4,9}; arr=null; try { int a=Arrmax(arr); System.out.println(a); System.out.println("程序正常运行"); } catch (Exception e) { e.printStackTrace(); System.out.println("数组出问题了"); } } private static int Arrmax(int[] arr) { if (arr==null){ throw new ArrayIsEmptyException("数组不能为空!"); } int max=arr[0]; for (int i = 0; i < arr.length; i++) { max=max>arr[i]?max:arr[i]; } return max; } /********************************************************/ /***********************编译时异常************************/ // 1、必须让这个类继承自Exception,才能成为一个编译时异常类。 public class AgeSpillException extends Exception{ public AgeSpillException() { } public AgeSpillException(String message) { super(message); } } /*************************************************************/ public class ExceptionTest2 { public static void main(String[] args) { int a=222; try { Age(a); System.out.println("your age is "+a); } catch (AgeSpillException e) { e.printStackTrace(); System.out.println("Age执行失败!!!"); } System.out.println("后续代码执行成功!!!"); } //2、在方法中对age进行判断,不合法则抛出AgeIllegalException private static void Age(int age) throws AgeSpillException{ if (age>0&&age<150){ System.out.println("信息保存成功"); }else { throw new AgeSpillException("Age limit,your age:"+age); } } }
3.异常处理
/* 比较好的做法: 1.将异常捕获,将比较友好的信息显示给用户看; 2.尝试重新执行,看是是否能修复这个问题。 */ public class ExceptionTest3 { public static void main(String[] args) { try { test1(); } catch (FileNotFoundException e) { System.out.println("您要找的文件不存在!!"); e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。 } catch (ParseException e) { System.out.println("您要解析的时间有问题了!"); e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。 } } /******************************************************************/ public static void test1() throws FileNotFoundException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse("2028-11-11 10:24:11"); System.out.println(d); test2(); } /**************************************************************/ public static void test2() throws FileNotFoundException { // 读取文件的。 InputStream is = new FileInputStream("D:/meinv.png"); } }
十三集合概述和分类
1.集合的分类
/*集合分为:1.Collection单列集合(集合元素是一个一个的) 2.Map双列集合(元素是一对一对的) Collection代表单列集合,每个元素(数据)只包含一个值 Map代表双列集合,每个元素包含两个值(键值对)。 Collection是单列集合的根接口,Collection接口下面又有两个子接口List接口、Set接口,List和Set下面分别有不同的实现类 */
/* List系列集合:添加的元素是有序、可重复、有索引。 ArrayList、LinekdList:有序、可重复、有索引。 Set系列集合:添加的元素是无序、不重复、无索引。 HashSet:无序、不重复、无索引; LinkedHashSet:有序、不重复、无索引。 TreeSet:按照大小默认升序排序、不重复、无索引。 */ //简单确认一下Collection集合的特点 ArrayList<String> list = new ArrayList<>(); //存取顺序一致,可以重复,有索引 list.add("java1"); list.add("java2"); list.add("java1"); list.add("java2"); System.out.println(list); //[java1, java2, java1, java2] HashSet<String> list = new HashSet<>(); //存取顺序不一致,不重复,无索引 list.add("java1"); list.add("java2"); list.add("java1"); list.add("java2"); list.add("java3"); System.out.println(list); //[java3, java2, java1]
2.Collection集合的常用方法
Collection<String> c = new ArrayList<>(); //1.public boolean add(E e): 添加元素到集合 c.add("java1"); c.add("java1"); c.add("java2"); c.add("java2"); c.add("java3"); System.out.println(c); //打印: [java1, java1, java2, java2, java3] //2.public int size(): 获取集合的大小 System.out.println(c.size()); //5 //3.public boolean contains(Object obj): 判断集合中是否包含某个元素 System.out.println(c.contains("java1")); //true System.out.println(c.contains("Java1")); //false //4.pubilc boolean remove(E e): 删除某个元素,如果有多个重复元素只能删除第一个 System.out.println(c.remove("java1")); //true System.out.println(c); //打印: [java1,java2, java2, java3] //5.public void clear(): 清空集合的元素 c.clear(); System.out.println(c); //打印:[] //6.public boolean isEmpty(): 判断集合是否为空 是空返回true 反之返回false System.out.println(c.isEmpty()); //true //7.public Object[] toArray(): 把集合转换为数组 Object[] array = c.toArray(); System.out.println(Arrays.toString(array)); //[java1,java2, java2, java3] //8.如果想把集合转换为指定类型的数组,可以使用下面的代码 String[] array1 = c.toArray(new String[c.size()]); System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3] //9.还可以把一个集合中的元素,添加到另一个集合中 Collection<String> c1 = new ArrayList<>(); c1.add("java1"); c1.add("java2"); Collection<String> c2 = new ArrayList<>(); c2.add("java3"); c2.add("java4"); c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去 System.out.println(c1); //[java1, java2, java3, java4]
//Collection集合的常用功能,ArrayList、LinkedList、HashSet、LinkedHashSet、TreeSet集合都可以调用 public boolean add(E e)//把给定的对象添加到当前集合中 public void clear()//清空集合所有的元素 public boolean remove(E e)//把给定的对象在当前集合中删除 public boolean contains(Object obj)//判断当前集合中是否包含给定的对象 public boolean isEmpty()//判断当前集合是否为空 public int size()//返回集合中元素的个数 public Object[] toArray()//把集合中的元素,存储到数组中
3.Collection(迭代器)遍历方式
List集合有索引,而Set集合没有索引,同意
Collection<String> c = new ArrayList<>(); c.add("赵敏"); c.add("小昭"); c.add("素素"); c.add("灭绝"); System.out.println(c); //[赵敏, 小昭, 素素, 灭绝] //第一步:先获取迭代器对象 //解释:Iterator就是迭代器对象,用于遍历集合的工具) Iterator<String> it = c.iterator(); //第二步:用于判断当前位置是否有元素可以获取 //解释:hasNext()方法返回true,说明有元素可以获取;反之没有 while(it.hasNext()){ //第三步:获取当前位置的元素,然后自动指向下一个元素. String e = it.next(); System.out.println(s); } //迭代器代码的原理如下: /* 当调用iterator()方法获取迭代器时,当前指向第一个元素 hasNext()方法则判断这个位置是否有元素,如果有则返回true,进入循环。 调用next()方法获取元素,并将当前元素指向下一个位置。 等下一次循环时,则获取下一个元素,依此内推 */ Iterator<String> it = lists.iterator(); while(it.hasNext()){ String ele=it.next(); System.out.println(ele); } //迭代器遍历集合的方法有 Iterator<E> iterator()//返回集合中的迭代器对象默认指向当前集合的第一个元素 boolean hasNext()//询问当前位置是否有元素存在,存在返回true,不存在返回false E next() //获取当前位置的元素,并同时将迭代器对象指向下一个元素处
4.增强for遍历集合
//增强for循环 /* 格式:for(元素的数据类型 变量名:数组或者集合){ } */ //增强for不光可以遍历集合,还可以遍历数组 /************************************************/ Collection<String> c = new ArrayList<>(); c.add("赵敏"); c.add("小昭"); c.add("素素"); c.add("灭绝"); //1.使用增强for遍历集合 for(String s: c){ System.out.println(s); } //2.再尝试使用增强for遍历数组 String[] arr = {"迪丽热巴", "古力娜扎", "稀奇哈哈"}; for(String name: arr){ System.out.println(name); }
5.forEach遍历集合
default void forEach(Consumer<? super T> action)//结合Lambda遍历集合 //forEach方法的参数是一个Consumer接口,而Consumer是一个函数式接口,所以可以传递Lambda表达式 /**********************************************************/ Collection<String> c = new ArrayList<>(); c.add("赵敏"); c.add("小昭"); c.add("素素"); c.add("灭绝"); //调用forEach方法 //由于参数是一个Consumer接口,所以可以传递匿名内部类 c.forEach(new Consumer<String>{ @Override public void accept(String s){ System.out.println(s); } }); //也可以使用lambda表达式对匿名内部类进行简化 c.forEach(s->System.out.println(s)); //[赵敏, 小昭, 素素, 灭绝]
6.Set集合
//Set集合是Collection的另一个分支的集合 /* Set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致;不重复;无索引。 */ // HashSet:无序、不重复、无索引。 // LinkedHashSet:有序、不重复、无索引。 // TreeSet:排序、不重复、无索引。 /* Set要用到的常用方法,基本上就是Collection提供的!! 自己几乎没有额外新增一些常用功能。 */
/* 哈希值 就是一个int型的数值,Java中每个对象都有一个哈希值。哈希值是对象的内存地址经过处理后的结构,由于对象的内存地址都不一样,所以哈希值也不一样。这里的内存地址是jvm虚拟机虚拟出来的地址,并不是真实的物理内存地址,C++的地址是真是的物理内存地址。 Java中的所有对象,都可以调用Object类提供的hasCode方法返回该对象自己的哈希值。 public int hashCode():返回对象的哈希码值。 对象哈希值的特点 同一个对象多次调用hashCode()方法返回的哈希值是相同的。 不同对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞) */ /*HashSet集合的底层原理 基于哈希表实现。 哈希表是一种增删改查数据,性能都较好的数据结构。 Hashset集合默认不能对内容一样的两个不同对象去重 可以存两个相同的对象。 */ /* 哈希表 jdk8之前,哈希表=数组+链表 jdk8开始,哈希表=数组+链表+红黑树 */ /* 1.创建一个默认长度16的数组,默认加载因子为0.75,数组名table 2.使用元素的哈希值对数组的长度求余计算出应存入的位置 3.判断当前位置是否为null,如果是null直接存入 4.如果不为null,表示有元素,则调用equals方法比较 相等,则不存;不相等,则存入数组 jdk8之前,新元素存入数组,占老元素位置,老元素挂下面 jdk8开始之后,新元素挂在老元素下面。 jdk8开始之后,哈希表引入了红黑树后,进一步提高了操作数据的性能。 */ /********************************************************/ //LinkedHashSet底层原理 /* 依然是基于哈希表(数组、链表、红黑树)实现的。 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。 */ /*********************************************************************/ //TreeSet /* 特点:不重复,无索引、可排序(默认升序排序,按照元素大小,由小到大排序) 底层是基于红黑树实现的排序。 注意: 1.对于数值类型:Integer,Double,默认按照数值本身的大小进行升序排序。 2.对于字符串类型:默认按照首字符的编号升序排序。 3.对于自定义类型如Student对象,TreeSet默认是无法直接排序的 自定义排序规则 TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。 方式一: 让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。 方式二: 通过调用TreeSet集合有参构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)。 public TreeSet(Comparator<? super E> compartor) */
7.集合并发修改异常
/* 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。 */ /************************************************************/ // 怎么保证遍历集合同时删除数据时不出bug? /* 使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i--操作。 */
8.Collection工具类
//前置内容 /* 可变参数:就是一种特殊的形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称; 可变参数的特点和好处 特点:可以不传参数据给他;可以传一个或者多个数据给它;也可以传一个数组给它。 好处:常常用来灵活的接受数据。 */ public static void main(String[] args) { int[] a={3,45,2,1,3}; show();//不传数据 show(10);//传入单个数据 show(1,2,3,4,6,5,43,2);//传入多个数据 show(a);//传入数组 } //注意事项: //1.一个形参列表中只能有一个可变参数。 //2.可变参数必须放在形参列表的最后面。 public static void show(int...a){ //a 本质为int型的数组 System.out.println(Arrays.toString(a)); System.out.println("------------------"); } /*********************************************************/ public static <T> boolean addAll(Collection<? super T> c,T... elements)//给集合批量添加元素 public static void shuffle(List<?> list)//打乱集合中的元素顺序 public static <T> void sort(List<T> list)//对集合中的元素进行升序排序 public static <T> void sort(List<T> list ,Comparator<? super T> c)//对list集合中元素,按照比较器对象指定的规则进行排序。 /***************************************************************/ public class CollectionsTest{ public static void main(String[] args){ //1.public static <T> boolean addAll(Collection<? super T> c, T...e) List<String> names = new ArrayList<>(); Collections.addAll(names, "张三","王五","李四", "张麻子"); System.out.println(names); //2.public static void shuffle(List<?> list):对集合打乱顺序 Collections.shuffle(names); System.out.println(names); //3.public static <T> void short(List<T list): 对List集合排序 List<Integer> list = new ArrayList<>(); list.add(3); list.add(5); list.add(2); Collections.sort(list); System.out.println(list); } } /***********************************************************/ //排序方式1:让元素实现Comparable接口,重写compareTo方法 public class Student implements Comparable<Student>{ private String name; private int age; private double height; //排序时:底层会自动调用此方法,this和o表示需要比较的两个对象 @Override public int compareTo(Student o){ //需求:按照年龄升序排序 //如果返回正数:说明左边对象的年龄>右边对象的年龄 //如果返回负数:说明左边对象的年龄<右边对象的年龄, //如果返回0:说明左边对象的年龄和右边对象的年龄相同 return this.age - o.age; } //...getter、setter、constructor.. } //3.public static <T> void short(List<T list): 对List集合排序 List<Student> students = new ArrayList<>(); students.add(new Student("蜘蛛精",23,169.7)); students.add(new Student("紫霞",22,169.8)); students.add(new Student("紫霞",22,169.8)); students.add(new Student("至尊宝",26,169.5)); /* 原理:sort方法底层会遍历students集合中的每一个元素,采用排序算法,将任意两个元素两两比较; 每次比较时,会用一个Student对象调用compareTo方法和另一个Student对象进行比较; 根据compareTo方法返回的结果是正数、负数,零来决定谁大,谁小,谁相等,重新排序元素的位置 注意:这些都是sort方法底层自动完成的,想要完全理解,必须要懂排序算法才行; */ Collections.sort(students); System.out.println(students); /****************************************************************/ //排序方式2:使用调用sort方法是,传递比较器(推荐使用) /* 原理:sort方法底层会遍历students集合中的每一个元素,采用排序算法,将任意两个元素两两比较; 每次比较,会将比较的两个元素传递给Comparator比较器对象的compare方法的两个参数o1和o2, 根据compare方法的返回结果是正数,负数,或者0来决定谁大,谁小,谁相等,重新排序元素的位置 注意:这些都是sort方法底层自动完成的,不需要我们完全理解,想要理解它必须要懂排序算法才行. */ Collections.sort(students, new Comparator<Student>(){ @Override public int compare(Student o1, Student o2){ return o1.getAge()-o2.getAge(); } }); System.out.println(students);
十四、Map双列集合
1.Map概述体系
Map集合中的每一个元素是以key=value的形式存在的,一个key=value就称之为一个键值对,而且在Java中有一个类叫Entry类,Entry的对象用来表示键值对对象。
所有的Map集合有如下的特点:
键不能重复,值可以重复,每一个键只能找到自己对应的值。
Map集合体系 (接口) Map<K,V>分为:HashMap<K,V> LinkedHashMap<K,V> TreeMap<K,V> ......... Map集合体系的特点 注意:Map系列集合的特点都是由键决定,值只是一个附属品,值是不做要求的 HashMap(由键决定特点):无序、不重复、无索引;(用的最多) LinkedHashMap(由键决定特点):有序、不重复、无索引; TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
2.Map集合的常用的方法
public V put(K key,V value)//添加元素 public int size()//获取集合大小 public void clear()//清空集合 public boolean isEmpty()//判断集合是否为空,为空返回true。反之返回false public V get(Object key)//根据键获取对应值 public V remove(Object key)//根据键删除整个元素 public boolean containsKey(Object value)//判断是否包含某个键 public boolean containsValue(Object value)//判断是否包含某个值 public Set(K) keySet() //获取全部键的集合 public Collection<V> values()//获取Map集合的全部值 /*************************************************************/ public class MapTest2 { public static void main(String[] args) { // 1.添加元素: 无序,不重复,无索引。 Map<String, Integer> map = new HashMap<>(); map.put("手表", 100); map.put("手表", 220); map.put("手机", 2); map.put("Java", 2); map.put(null, null); System.out.println(map); // map = {null=null, 手表=220, Java=2, 手机=2} // 2.public int size():获取集合的大小 System.out.println(map.size()); // 3、public void clear():清空集合 //map.clear(); //System.out.println(map); // 4.public boolean isEmpty(): 判断集合是否为空,为空返回true ,反之! System.out.println(map.isEmpty()); // 5.public V get(Object key):根据键获取对应值 int v1 = map.get("手表"); System.out.println(v1); System.out.println(map.get("手机")); // 2 System.out.println(map.get("张三")); // null // 6. public V remove(Object key):根据键删除整个元素(删除键会返回键的值) System.out.println(map.remove("手表")); System.out.println(map); // 7.public boolean containsKey(Object key): 判断是否包含某个键 ,包含返回true ,反之 System.out.println(map.containsKey("手表")); // false System.out.println(map.containsKey("手机")); // true System.out.println(map.containsKey("java")); // false System.out.println(map.containsKey("Java")); // true // 8.public boolean containsValue(Object value): 判断是否包含某个值。 System.out.println(map.containsValue(2)); // true System.out.println(map.containsValue("2")); // false // 9.public Set<K> keySet(): 获取Map集合的全部键。 Set<String> keys = map.keySet(); System.out.println(keys); // 10.public Collection<V> values(); 获取Map集合的全部值。 Collection<Integer> values = map.values(); System.out.println(values); // 11.把其他Map集合的数据倒入到自己集合中来。(拓展) Map<String, Integer> map1 = new HashMap<>(); map1.put("java1", 10); map1.put("java2", 20); Map<String, Integer> map2 = new HashMap<>(); map2.put("java3", 10); map2.put("java2", 222); map1.putAll(map2); // putAll:把map2集合中的元素全部倒入一份到map1集合中去。 System.out.println(map1); System.out.println(map2); } }
3.Map集合遍历方式
Map集合一共有三种遍历方式: &&第一种键找值: 先获取Map集合全部的键,在通过遍历键来找值。 方法: public Set<K> keySet()//获取所有键的集合 public V get(Object key)//根据键获取其对应的值。 /*********************************************/ Map<String,Integer> sim = new HashMap<>(); sim.put("女妖精",24); sim.put("男妖精",23); sim.put("猴精",24); sim.put("猪妖精",24); /***********************************************/ Set<String> ss = sim.keySet(); Iterator<String> ir = ss.iterator(); while (ir.hasNext()){ String s = ir.next(); System.out.println(s+" "+sim.get(s)); } System.out.println("------------------------------"); for (Map.Entry<String, Integer> n : en) { System.out.println(n.getKey()+" "+n.getValue()); } /***************************************************/ &&第二种方式:直接获取每一个Entry对象,把Entry存储扫Set集合中去,再通过Entry对象获取键和值。 方法: Set<Map,Entry<K,V> entrySet()//获取所有“键值对”的集合。 /******************************************************/ Set<Map.Entry<String, Integer>> en = sim.entrySet(); Iterator<Map.Entry<String, Integer>> it = en.iterator(); while (it.hasNext()) { Map.Entry<String, Integer> n = it.next(); System.out.println(n.getKey()+" "+n.getValue()); } System.out.println("------------------------------"); for (Map.Entry<String, Integer> n : en) { System.out.println(n.getKey()+" "+n.getValue()); } /*************************************************************/ &&&Map集合的第三种遍历方式,jdk8之后才有的,需要用到下面的一个方法forEach 方法: default void forEach(BiConsumer<? super K,? super V> action)//结合Lambda遍历Map集合 /****************************************/ sim.forEach(new BiConsumer<String, Integer>() { @Override public void accept(String s, Integer integer) { System.out.println(s+" "+integer); } }); /****************************************************/ &&&Map集合最普通的遍历 System.out.println("----------------------------------"); Set<String> ss = sim.keySet(); Iterator<String> ir = ss.iterator(); while (ir.hasNext()){ String s = ir.next(); System.out.println(s+" "+sim.get(s)); }
4.HashMap
它的键是无序、不能重复,而且没有索引的。 HashMap底层数据结构: 哈希表结构 JDK8之前的哈希表 = 数组+链表 JDK8之后的哈希表 = 数组+链表+红黑树 哈希表是一种增删改查数据,性能相对都较好的数据结构 往HashMap集合中键值对数据时,底层步骤如下 第1步:当你第一次往HashMap集合中存储键值对时,底层会创建一个长度为16的数组 第2步:把键然后将键和值封装成一个对象,叫做Entry对象 第3步:再根据Entry对象的键计算hashCode值(和值无关) 第4步:利用hashCode值和数组的长度做一个类似求余数的算法,会得到一个索引位置 第5步:判断这个索引的位置是否为null,如果为null,就直接将这个Entry对象存储到这个索引位置 如果不为null,则还需要进行第6步的判断 第6步:继续调用equals方法判断两个对象键是否相同 如果equals返回false,则以链表的形式往下挂 如果equals方法true,则认为键重复,此时新的键值对会替换就的键值对。 /****************************************************************/ HashMap底层需要注意这几点: 1.底层数组默认长度为16,如果数组中有超过12个位置已经存储了元素,则会对数组进行扩容2倍 数组扩容的加载因子是0.75,意思是:16*0.75=12 2.数组的同一个索引位置有多个元素、并且在8个元素以内(包括8),则以链表的形式存储 JDK7版本:链表采用头插法(新元素往链表的头部添加) JDK8版本:链表采用尾插法(新元素我那个链表的尾部添加) 3.数组的同一个索引位置有多个元素、并且超过了8个,则以红黑树形式存储 /*********************************************************************************/ public class Student implements Comparable<Student> { private String name; private int age; private double height; // this o @Override public int compareTo(Student o) { return this.age - o.age; // 年龄升序排序 } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age, height); } public Student() { } public Student(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } //...get,set方法自己补全.... @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } } /***********************************************************/ /** * 目标:掌握Map集合下的实现类:HashMap集合的底层原理。 */ public class Test1HashMap { public static void main(String[] args) { Map<Student, String> map = new HashMap<>(); map.put(new Student("蜘蛛精", 25, 168.5), "盘丝洞"); map.put(new Student("蜘蛛精", 25, 168.5), "水帘洞"); map.put(new Student("至尊宝", 23, 163.5), "水帘洞"); map.put(new Student("牛魔王", 28, 183.5), "牛头山"); System.out.println(map); } } 注意:&&决定HashMap的键是否重复依赖与两个方法,一个是hashCode方法、一个是equals方法。如果键的hashCode值相同,并且属性使用equals比较为true,就认为键重复。 &&Map集合中存储自定义对象作为键,为了保证键的唯一性,我们应该重写hashCode方法和equals方法。
5.LinkedHashMap
LinkedHashMap集合的特点也是由键决定的:有序的、不重复、无索引。 /**************************************************/ /** * 目标:掌握LinkedHashMap的底层原理。 */ public class Test2LinkedHashMap { public static void main(String[] args) { // Map<String, Integer> map = new HashMap<>(); // 按照键 无序,不重复,无索引。 LinkedHashMap<String, Integer> map = new LinkedHashMap<>(); // 按照键 有序,不重复,无索引。 map.put("手表", 100); map.put("手表", 220); map.put("手机", 2); map.put("Java", 2); map.put(null, null); System.out.println(map); } } //LinkedHashMap的底层原理,和LinkedHashSet底层原理是一样的。底层多个一个双向链表来维护键的存储顺序,取元素时,先取头节点元素,然后再依次取下一个几点,一直到尾结点。所以是有序的
6.TreeMap
TreeMap集合的特点也是由键决定的,默认按照键的升序排列,键不重复,也是无索引的。 TreeMap集合的底层原理和TreeSet也是一样的,底层都是红黑树实现的。所以可以对键进行排序。 往TreeMap集合中存储引用类型对象作为键,排序方法有两种。 /***************************************/ 排序方式1:写一个引用类,让引用类实现Comparable接口 /********************************************************/ //第一步:先让Student类,实现Comparable接口 public class Student implements Comparable<Student>{ private String name; private int age; private double height; //无参数构造方法 public Student(){} //全参数构造方法 public Student(String name, int age, double height){ this.name=name; this.age=age; this.height=height; } //...get、set、toString()方法自己补上.. //按照年龄进行比较,只需要在方法中让this.age和o.age相减就可以。 /* 原理: 在往TreeSet集合中添加元素时,add方法底层会调用compareTo方法,根据该方法的 结果是正数、负数、还是零,决定元素放在后面、前面还是不存。 */ @Override public int compareTo(Student o) { //this:表示将要添加进去的Student对象 //o: 表示集合中已有的Student对象 return this.age-o.age; } } /*********************************************************/ 排序方式2:在创建TreeMap集合时,直接传递Comparator比较器对象。 /*********************************************************/ /** * 目标:掌握TreeMap集合的使用。 */ public class Test3TreeMap { public static void main(String[] args) { Map<Student, String> map = new TreeMap<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return Double.compare(o1.getHeight(), o2.getHeight()); } }); // Map<Student, String> map = new TreeMap<>(( o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight())); map.put(new Student("蜘蛛精", 25, 168.5), "盘丝洞"); map.put(new Student("蜘蛛精", 25, 168.5), "水帘洞"); map.put(new Student("至尊宝", 23, 163.5), "水帘洞"); map.put(new Student("牛魔王", 28, 183.5), "牛头山"); System.out.println(map); } }
7.集合嵌入
//把一个集合当做元素,存储到另一个集合中去,我们把这种用法称之为集合嵌套。 /*************************************************************/ /** * 目标:理解集合的嵌套。 * 江苏省 = "南京市","扬州市","苏州市“,"无锡市","常州市" * 湖北省 = "武汉市","孝感市","十堰市","宜昌市","鄂州市" * 河北省 = "石家庄市","唐山市", "邢台市", "保定市", "张家口市" */ public class Test { public static void main(String[] args) { // 1、定义一个Map集合存储全部的省份信息,和其对应的城市信息。 Map<String, List<String>> map = new HashMap<>(); List<String> cities1 = new ArrayList<>(); Collections.addAll(cities1, "南京市","扬州市","苏州市" ,"无锡市","常州市"); map.put("江苏省", cities1); List<String> cities2 = new ArrayList<>(); Collections.addAll(cities2, "武汉市","孝感市","十堰市","宜昌市","鄂州市"); map.put("湖北省", cities2); List<String> cities3 = new ArrayList<>(); Collections.addAll(cities3, "石家庄市","唐山市", "邢台市", "保定市", "张家口市"); map.put("河北省", cities3); System.out.println(map); List<String> cities = map.get("湖北省"); for (String city : cities) { System.out.println(city); } map.forEach((p, c) -> { System.out.println(p + "----->" + c); }); } }
十五、递归
/* 递归从形式上来说就是自己调用自己 递归是一种算法,方法调用自身的形式称为递归 递归的形式自己调用自己 间接递归:方法调用其他方法,其他方法又回来调用方法自己 递归的效率比较低,因为方法需要不断地进栈出栈。 递归的方法比较浪费内存的空间,栈的内存本身比较小,很容易将栈内存的内存空间耗尽,那么程序就无法继续执行下去,报出一个错误:StackOverflowError 栈内存溢出。 递归一定要有出口,不然就是死循环; 构造方法不能递归使用。 递归如果没有控制好终止,会出现递归死循环,会导致内存溢出的错误。 */ public static void main(String[] args) { System.out.println(getSum(5)); } private static int getSum(int n) { if (n==0) return n; return n+getSum(n-1); }