第三部分-应用
一.常见异常
1.Java把不同异常用不同的类表示。
2.异常还可以分为编译异常和运行异常
3.异常的体系结构
java.lang.Throwable:异常体系的根父类
java.lang.Error:错误
java.lang.Exception:异常
4.常见运行异常
public class ErrorTest { //ArrayIndexOutOfBoundsException @Test public void test1(){ int[]a=new int[12]; System.out.println(a[12]); } //nullPointerException @Test public void test2(){ String[] s=new String[14]; s=null; System.out.println(s[1]); } //ClassCastException @Test public void test3(){ Object obj=new String(); Date date =(Date) obj; } //NumberFormatException @Test public void test4(){ String str="abc"; int i=Integer.parseInt(str); System.out.println(i); } //InputMismatchException @Test public void test5(){ Scanner scanner=new Scanner(System.in); int num=scanner.nextInt(); System.out.println(num); } //ArithmeticException @Test public void test6(){ int r=45; System.out.println(r/0); } }
5.常见编译异常:classnotfoutexception,filenofoundexception,ioexception
二.异常处理
1.方式一
1.异常处理方式:
方式一:try-catch-finally
方式二:throws+异常处理
2.对于运行时异常通常不进行显示处理,直接处理代码
public void test4(){ try { String str = "abc"; int i = Integer.parseInt(str); System.out.println(i); }catch (NumberFormatException e){ e.printStackTrace(); } System.out.println("程序结束"); }
3.对于编译式异常要进行处理
4.finally:把一定要执行的代码放在finally结构中。即使try或catch中有未使用的异常。有些资源(输入输出流,等)在使用完后要显示进行关闭操作。否则会导致内存泄漏。
5.try-catch结构可以嵌套。try中的内容只在内部起作用
2.方式二
1.throws:在方法的声明处采用throws 异常类型1,异常类型2
2.方法重写中子类异常要是父类异常的子类或者相同。如果父类没有异常,那么子类必须采用try-catch处理
3.手动抛出,用throw
4.throw和throws的区别
前者在方法内,抛出异常类的对象。
后者在方法的声明处,用来处理异常,将异常向上传了。
上流排污,下流治污。
5.为什么需要自定义异常:见名知意。
6.如何构造自定义异常
public class BelowZeroException extends Exception { static final long serialVersionUID = -3387993124229948L; public BelowZeroException() { super(); } public BelowZeroException(String message) { super(message); } public BelowZeroException(String message, Throwable cause) { super(message, cause); } }
3.本章复习例题
public class DivisionDemo { public static void main(String[] args) { try { int m=Integer.parseInt(args[0]); int n=Integer.parseInt(args[1]); int result=divide(m,n); System.out.println("结果为"+result); } catch (BelowZeroException1 e) { System.out.println(e.getMessage()); }catch (NumberFormatException e){ System.out.println("数据类型不一致"); }catch (ArrayIndexOutOfBoundsException e){ System.out.println("下标越界"); }catch (ArithmeticException e){ System.out.println("除零"); } } public static int divide(int m,int n)throws BelowZeroException1{ if(m<0||n<0){ throw new BelowZeroException1("输入负数了"); } return m/n; }
三.多线程
1.基本概念
1.程序:是一段静态的代码
进程:正在运行的应用程序是操作系统调度和分配资源的最小的单位。
线程:线程是程序内部的一条执行路径,是cpu调度和执行的最小单位。
2.单核与多核
3.并行:同一时间多件事共同完成,好像有多核运行
并发:单核快速切换。
2.线程的创建
方式一:
①创建一个继承于Thread的子类
②重写run方法,将线程要执行的操作声明在方法体内
③创建该子类的对象
④利用子类对象调用start()方法。
public class ThreadTest2 { public static void main(String[] args) { new Thread(){ public void run() { for(int i=1;i<100;i++) { if(i%2!=0) System.out.println(Thread.currentThread().getName()+"-->"+i); } } }.start(); new Thread(){ public void run() { for(int i=1;i<100;i++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+"---->"+i); } } }.start(); } } class OddPrint extends Thread{ public void run(){ for(int i=1;i<100;i++) { if(i%2!=0) System.out.println(Thread.currentThread().getName()+i); } } } class EvenPrint extends Thread{ public void run(){ for(int i=1;i<100;i++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+i); } } }
方式二:
①创建一个实现Runnable接口的子类
②重写run方法,方法体内为要实现的功能
③创建该子类的对象
④将子类对象作为创建Thread的实例的参数。
⑤用Thread的实例去调用start方法。
public static void main(String[] args) { EvenNuberPrint4 e4=new EvenNuberPrint4(); for(int i=1;i<100;i++) { if(i%2!=0) System.out.println(Thread.currentThread().getName()+"++++++++++++++++++++"+i); } new Thread(new Runnable() { @Override public void run() { for(int i=1;i<100;i++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+"============="+i); } } }).start();//这一段不错哦。匿名接口实现类的匿名对象。 new Thread(e4).start(); } } class EvenNuberPrint4 implements Runnable{ @Override public void run() { for(int i=1;i<100;i++) { if(i%2==0) System.out.println(Thread.currentThread().getName()+i); } }
联系:public class Thread implement Runtime。
推荐使用方法二
start调用的是Thread类中的run方法。
3.线程结构
1.线程中的构造器
public Thread ();
public Thread(String name);
public Thread(Runnable target)
public Thread(String name,Runnable target)
2.线程中的方法
start()
run()
currentThread():获取当前线程
getname():
setname():设置线程名
sleep(long millis):静态方法
yiled():释放cpu的执行权
join():见名知意,谁调用该方法的将优先完成线程。
isAlive(): 判断是否线程存活。
3.线程的优先级
getpriority()默认为5
setpriority () 可设置1到10
4.线程的生命周期
1.新建——runnable——死亡。
Blocking--Waiting--Timedwaited
5.线程的安全问题
1.例如,卖票问题,出现重票和错票。
6.如何解决线程安全问题
方法一:同步代码块
synchronized(同步监视器){
//同步的代码
}同步监视器又称为锁,可以使用任何一个类的对象充当。但多个线程必须使用同一同步监视器。实现Runnable接口的方式时,考虑使用this。在继承Thread类的方式时,慎用this,可以考虑使用当前类.class
public class SafeTest { public static void main(String[] args) { SaleTicket s=new SaleTicket(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.setName("Windows 1"); t2.setName("Windows 2"); t3.setName("Windows 3"); t1.start(); t2.start(); t3.start(); } } class SaleTicket implements Runnable{ int ticket =100; Object obj =new Object(); @Override public void run() { while (true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(obj){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"票号为:"+ticket); ticket--; } else { break; } } } } }
方法二:同步方法
public class SafeTest1 { public static void main(String[] args) { SaleTicket1 s=new SaleTicket1(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.setName("Windows 1"); t2.setName("Windows 2"); t3.setName("Windows 3"); t1.start(); t2.start(); t3.start(); } } class SaleTicket1 implements Runnable{ boolean isFlag=true; int ticket =100; Object obj =new Object(); @Override public void run() { while (isFlag){ show(); } } public synchronized void show(){//此题目为s,仍然唯一 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(ticket>0){ System.out.println(Thread.currentThread().getName()+"票号为:"+ticket); ticket--; } else { isFlag=false; } } }
7.解决懒汉式的多线程问题
package chapter10.exer6; public class BankTest { static Bank b1=null; static Bank b2=null; public static void main(String[] args) { Thread t1=new Thread(){ @Override public void run() { b1=Bank.getInstance(); } }; Thread t2=new Thread(){ @Override public void run() { b2=Bank.getInstance(); } }; t1.start(); t2.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(b1); System.out.println(b2); System.out.println(b1==b2); } } class Bank { public Bank() { } private static volatile Bank instance = null; public static synchronized Bank getInstance(){ if(instance==null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } instance=new Bank(); } return instance; } }
8.锁(Lock)的使用
1.创建lock的实例,声明为static final
2.执行lock()的方法,锁定调用
3.执行unlock()的调用,释放锁定
对比synchronized与lock
package chapter10.exer6; import java.awt.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockTest { public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); Window w3 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } } class Window extends Thread{ static int ticket = 100; //1. 创建Lock的实例,需要确保多个线程共用同一个Lock实例!需要考虑将此对象声明为static final private static final ReentrantLock lock =new ReentrantLock(); @Override public void run() { while(true){ try{ //2. 执行lock()方法,锁定对共享资源的调用 lock.lock(); if(ticket > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket); ticket--; }else{ break; } }finally{ //3. unlock()的调用,释放对共享数据的锁定 lock.unlock(); } } } }
9.线程的通信
1.使多线程有规律的工作
2.wait(),notify() ,notifyAll()这三个方法的使用是在同步代码块和同步方法中
这三个方法的调用者必须是同步监视器。否则报异常。这三个方法都在object类中
3.对比wait()和sleep()
10.复习案例
package chapter10.exer7; import java.awt.*; public class ThreadTestFinal { public static void main(String[] args) { Clock clock=new Clock(); Produce p1=new Produce(clock); Consume c1=new Consume(clock); Consume c2=new Consume(clock); p1.setName("生产一车间"); c1.setName("消费一车间"); c2.setName("消费二车间"); p1.start(); c1.start(); c2.start(); } } class Clock{ private int product=0; public synchronized void addproduct() { if (product >= 20) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { product++; System.out.println(Thread.currentThread().getName() + "生产了第" + product + "个"); notifyAll(); } } public synchronized void minproduct(){ if(product<=0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + "消费了第" + product + "个"); product--; notifyAll(); } } } class Produce extends Thread { private Clock clock; public Produce(Clock clock) { this.clock = clock; } @Override public void run() { while (true) { System.out.println("开始生产>>>>>>"); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } clock.addproduct(); } } } class Consume extends Thread { private Clock clock; public Consume(Clock clock) { this.clock = clock; } @Override public void run() { while (true) { System.out.println("开始消费<<<<"); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } clock.minproduct(); } } }
11.创建多线程的另外两种方式
四.常用类和基础API
1.String
string的不可变性:脑中要有字符串常量池和栈和堆的空间位置图。字符串常量池中只能有一个相同的。
Strng实例化的方式:Stirng s1=new String("hello");创建了2个对象,一个是value一个是内容hello。
public void test2(){ String s1="hello";//S1是变量,S2是常量。 String s2="helloworld"; String s3=s1+"world"; String s4=s3.intern();//返回字符串常量池中字面量的地址 System.out.println(s2==s4); }
concat:都会new一个对象
2.String的构造器和常用方法
1.字符集UTF-8:一个汉字对应3个字符。
字符集gbk:一个汉字对应2个字符。它们都同时兼容assci码,字母都占用一个字节。
2.编码与解码:解码时使用的字符集和编码时要一致。否则会乱码
3.String常用的一些方法
//string和char[]类型的转换 @Test public void test1(){ String str="hello"; char[] arr = str.toCharArray(); for (char c : arr) { System.out.println(c); } String str1=new String(arr); System.out.println(str1); }
4.算法题(寻找两个字符串中的最大子串)
@Test public void test4(){ String s1="ghoaihrohr"; String s2="gholwijhgoighrowshaihrohr"; System.out.println(maxsubstr(s1, s2)); } public String maxsubstr(String s1,String s2){ if(s1!=null&&s2!=null){ String maxstr=(s1.length()>s2.length())?s1:s2; String minstr=(s1.length()>s2.length())?s2:s1; int len=minstr.length(); for(int i=0;i<len;i++){ for(int x=0, y=len-i;y<len;x++,y++){ if(maxstr.contains(minstr.substring(x,y))){ return minstr.substring(x,y); } } } } return null; }
5.练习登入界面
package chapter11.StringTest; /* 16点49分begin 17点07分finish。 事实证明一直听课就像被灌输。自己来敲还是很舒服的 完成简单的登入界面 */ import java.util.Scanner; public class Demo4 { public static void main(String[] args) { User[] arr=new User[3];//就是这样创建数组就可以 arr[0]=new User("TOM","98473"); arr[1]=new User("Mike","98347983473"); arr[2]=new User("Eric","9847983473"); System.out.println("库中的用户有:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i].toString()); } Scanner scan=new Scanner(System.in); System.out.println("请输入账号:"); String s1=scan.next(); System.out.println("请输入密码:"); String s2=scan.next(); boolean flag=true; for (int i = 0; i < arr.length; i++) { if(s1.equals(arr[i].getCount())) { flag=false; if(s2.equals(arr[i].getPassport())){ System.out.println("登入成功!"+arr[i].getCount()); } else{ System.out.println("密码输入有误!请重新输入"); } break; } } if(flag){ System.out.println("没有该用户"); } scan.close();//不要忘了关闭 } } class User{ private String count; private String passport; public String getCount() { return count; } public void setCount(String count) { this.count = count; } public String getPassport() { return passport; } public void setPassport(String passport) { this.passport = passport; } public User() { } public User(String count, String passport) { this.count = count; this.passport = passport; } @Override public String toString() { return count+"-"+passport; } }
3.StringBuilder
1.string:可变字符序列
stringbuilder:不可变字符序列,效率高,有线程安全问题
stringbuffer:不可变字符序列,效率低,无线程安全问题
一般而言stingbulider和stringbuffer功能类似,倾向于使用stringbuilder
2.如果开发需要频繁针对于字符串进行增删改查,建议使用stringbuilder或者stringbuffer。
3.常用的一些方法
增:删:改:查:插:
4.Date
1.两种常见的Date
public class Teat101 { @Test public void test1(){ Date date1=new Date(); System.out.println(date1); long time = date1.getTime(); Date date3=new Date(time); System.out.println(time); }@Test public void test2(){ java.sql.Date date2=new java.sql.Date(293780598273L); System.out.println(date2.toString()); } }
5.simpledateformat
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH;mm:ss");括号里面可以有多样,就是说格式化后的选择多样。
@Test public void test3() throws ParseException { Date date4=new Date(); SimpleDateFormat sdf=new SimpleDateFormat();//这是空参的 //格式化 String s = sdf.format(date4); System.out.println(s); //解析 Date date4Back = sdf.parse(s); System.out.println(date4Back); }
6.Calendar
1.是一个抽象类,而创建子类实例,我们是通过Calendar的静态方法getInstance()
来获取.
7.JDK8以后新的API
1.三个类:localdate,localtime,localdatetime
2.实例化的方式:now(),of()。
public void test4(){ //now(),静态方法,获取当前时间和日期 LocalDate localDate=LocalDate.now(); System.out.println(localDate); LocalDateTime localDateTime=LocalDateTime.now(); System.out.println(localDateTime); System.out.println("----------"); //of(),静态方法,创建指定时间和日期 LocalDate localDate1=LocalDate.of(2021,10,21); System.out.println(localDate1); LocalDateTime localDateTime1=LocalDateTime.of(2013,4,21,21,34); System.out.println(localDateTime1); }
3.一些方法:例如withdayofmonth
int dayOfMonth = localDate.getDayOfMonth(); System.out.println(dayOfMonth); LocalDate localDate2 = localDate.withDayOfMonth(23); System.out.println(localDate2); LocalDate localDate3 =localDate.plusDays(12); System.out.println(localDate3);
4.instant-->类似于Date。
5.DateTimeFormatter-->类似于SimpleFormatDate
public void test5(){ //DateTimeFormatter的使用 // 创建一个日期时间对象 LocalDateTime dateTime = LocalDateTime.now(); // 定义格式模式 String pattern = "yyyy-MM-dd HH:mm:ss"; // 创建 DateTimeFormatter 对象 DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); // 格式化日期时间对象为字符串 String formattedDateTime = dateTime.format(formatter); System.out.println("格式化后的日期时间:" + formattedDateTime); // 解析字符串为日期时间对象 LocalDateTime parsedDateTime = LocalDateTime.parse(formattedDateTime, formatter); System.out.println("解析后的日期时间:" + parsedDateTime); }
8.compare
1.创建子类并实现comparable接口
2.重写compareto方法并确定比较标准
3.创建子类的对象调用方法进行比较即可。
//用comparable实现价格排序 //10点32分begin //10点41分finish public class Test1 { @Test public void test2(){ Product p1=new Product("xiaomi",4999); Product p2=new Product("huawei",9999); int i = p1.compareTo(p2); if(i>0){ System.out.println(p1.getName()+"大于"+p2.getName()); } else { System.out.println(p1.getName()+"小于"+p2.getName()); } } } class Product implements Comparable{ private String name; private double price; public Product() { } public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public int compareTo(Object o) { if(o==this){ return 0; } if(o instanceof Product){ Product p01=(Product) o; int value =Double.compare(this.price,p01.price); if(value!=0){ return value; } return this.name.compareTo(p01.name); } throw new RuntimeException("类型不匹配"); } }
9.comparator
1.创建comparator接口的实现类A。
2.创建类A的对象并重写comparator的抽象方法compare(object o1,object o2)
在此方法指明具体标准。
3.将对象传入到相应方法的参数位置。
下面的例子使用匿名对象做的,更加简单。
public class Demo1 { @Test public void test1(){ String[] arr1=new String[]{"lili","jake","mike","tom"}; Arrays.sort(arr1, new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof String&&o2 instanceof String){ String s1=(String) o1; String s2=(String) o2; return s1.compareTo(s2); } throw new RuntimeException("类型异常"); } }); for (String s : arr1) { System.out.println(s); } } }
10.其余api
1.system类
2.Runtime类
3.math类
遇到需要再上网查找搜索即可
五.集合框架
1.概述
1.java.util.Collection:存储一个个数据
子接口:List:存储有序的,可重复的数据。
Arraylist(主要实现类),LinkedList,Vector
其实Arraylist底层好像也是数组,集合相较数组的巨大优势在于可以调用许多方法。
子接口:Set 存储无序的,不可重复的数据。(类似高中的集合)
HashSet LinkedHashSet TreeSet
2.java.util.Map:存储一对对数据(键值对)
存储特点也是无序的,不可重复的。
HashMap(主要实现类),LinkedHashMap,TreeMap ,Hashtable,Properties
3.补充一些以前的知识点
1)对于==,比较的是值是否相等 如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等; 如果作用于引用类型的变量,则比较的是所指向的对象的地址 2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址; 诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
Arrays.tostring(arr):将数组转换为String类型输出。
4.集合和数组的相互转化
集合-->数组 toArray
数组到集合--> Arrays.aslist
public void test4(){ Collection c1=new ArrayList(); c1.add("aa"); c1.add("123"); c1.add("123"); c1.add(new String("javastudy")); Object[] array1 = c1.toArray();//集合——>>数组,显然只能是object类的数组 System.out.println(Arrays.toString(array1)); } @Test public void test5(){ String[] arr=new String[]{"sl","shfk","oisho"}; List list = Arrays.asList(arr);//通过方法将数组转化为集合 System.out.println(list); }
5.添加元素时:元素所属的类要重写equals()方法。
6.迭代器Iterator
Iterator iterator=c1.iterator();
while(iterator.hasnext()){
system.out.println(iterator.next());
}
public void test1(){ Collection c1=new ArrayList(); c1.add("aa"); c1.add("123"); c1.add("123"); c1.add(new String("javastudy")); //获取迭代器对象, Iterator iterator = c1.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }//输出集合元素 }
2.List子接口
1.看成动态数组,存贮有序的,可重复的。
2.常用方法:collection中声明的15个和可以利用角标的特殊几个
add(Object obj) addAll(Collection coll) clear() isEmpty() size() contains(Object obj) containsAll(Collection coll) retainAll(Collection coll);保留交集 remove(Object obj) removeAll(Collection coll) hashCode() equals() toArray()
iterator() ---> 引出了迭代器接口
集合与数组的相互转换: 集合 ---> 数组:toArray() 数组 ---> 集合:调用Arrays的静态方法asList(Object ...objs),返回的是List
Arrays.tostring:将数组变为String类型输出。
向Collection中添加元素的要求:
要求元素所属的类一定要重写equals()!(Object ... objs)
3.List是一个接口,有三个实现类.
LinkedList(底层使用双向链表)底层使用双向链表的方式进行存储;在对集合中的数据进行频繁的删除、插入操作时,建议使用此类在插入、删除数据时,效率较高;在添加数据、查找数据时,效率较低;
Vector(线程安全,效率低,底层使用object[])
ArrayList(线程不安全,效率高,底层使用object[])(主要使用!)底层使用Object[]数组存储 。 在添加数据、查找数据时,效率较高;在插入、删除数据时,效率较低
4复习好题:
package chapter12.exer2; /* 11:00分begin 模拟点歌系统 11点50分finish 收获满满 */ import org.junit.runners.Suite; import java.awt.*; import java.util.ArrayList; import java.util.Scanner; public class Demo8 { private static ArrayList musicList=new ArrayList(); private static Scanner sc=new Scanner(System.in); public static void main(String[] args) { primarymusic(); boolean flag=true; while(flag){ System.out.println("-------------欢迎来到点歌系统------------"); System.out.println("此时列表歌曲有:"+musicList); System.out.println("1.添加歌曲至列表"); System.out.println("2.将歌曲置顶"); System.out.println("3.将歌曲前移一位"); System.out.println("4.退出"); System.out.println("请输入1-4:"); int key=sc.nextInt(); switch (key){ case 1: addmusic(); break; case 2: settopmusic(); break; case 3: setbeforemusic(); break; case 4: System.out.println("点歌系统已退出!"); flag=false; break; } } } private static void primarymusic(){ musicList.add("Who says"); musicList.add("Love story"); musicList.add("Wonderful U"); musicList.add("Stay"); musicList.add("Wake"); musicList.add("最伟大的作品"); } private static void addmusic(){ System.out.println("请输入你想要添加的歌曲:"); String dummy=sc.nextLine();//会有换行操作,所以再加一个 String musicName=sc.nextLine();//果然,原来是next是将空格作为分隔,所以可以使用nextline() int musicIndex=musicList.indexOf(musicName); if(musicIndex<0){ musicList.add(musicName); System.out.println("已成功添加歌曲:"+musicName); } else { System.out.println("该歌曲已经在列表歌单里!"); } } private static void settopmusic(){ System.out.println("请选择你要置顶的歌曲:"); String dummy=sc.nextLine(); String musicName=sc.nextLine(); int musicIndex=musicList.indexOf(musicName); if(musicIndex<0){ System.out.println("该歌曲不在列表中!"); } else if(musicIndex==0){ System.out.println("该歌曲已经在置顶位置了!"); } else { musicList.remove(musicName); musicList.add(0,musicName); System.out.println("已成功将"+musicName+"置顶"); } } private static void setbeforemusic(){ System.out.println("请选择你要前移的歌曲:"); String dummy=sc.nextLine(); String musicName=sc.nextLine(); int musicIndex=musicList.indexOf(musicName); if(musicIndex<0){ System.out.println("该歌曲不在列表中!"); } else if(musicIndex==0){ System.out.println("该歌曲已经在置顶位置了!"); } else { musicList.remove(musicName); musicList.add(musicIndex-1,musicName); System.out.println("已成功将"+musicName+"前移"); } } }
3.Set子接口
1.存储无序的,不可重复的数据。
无序性:根据哈希值计算在数组中的存储位置。
2.三个实现类:HashSet (主要实现类),底层使用的是HashMap,使用数组和单向链表和红黑数组结构进行存贮。
LinkedHashSet:是HashSet的子类,在数组和单向链表和红黑数组结构基础上多了一组双向链表。
TreeSet:底层使用红黑树存储,可以按照添加元素的指定属性大小顺序进行遍历。
3.常用方法:collection中的15个抽象方法
4.添加HashSet和LinkedHashSet中元素的要求:
重写equals()和hashcode()方法,并使其保持一致。
5.TreeSet:
判断数据是否相等不考虑hashSet和equals()方法。因为底层逻辑不一样。
考虑标准变成自然排序compareTo()或者定制排序comoare()的返回值。
package chapter12.exer2; import com.sun.source.tree.Tree; import org.junit.Test; /* * 15:47begin * 16:20finish * 复习了compare和comparator两种比较 * */ import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; public class Demo9 { @Test public void test1(){ TreeSet treeSet=new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof Person&&o2 instanceof Person){ Person p2=(Person) o1; Person p3=(Person) o2; int value=p2.id- p3.id; if(value!=0){ return value; } return p2.name.compareTo(p3.name);//这里有点不确定 } throw new RuntimeException("类型不匹配"); } }); Person p1=new Person("lucy",32); Person p2=new Person("tom",31); Person p3=new Person("jake",12); Person p4=new Person("mike",23); treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); /* treeSet.add("aa"); treeSet.add("bb"); treeSet.add("234"); treeSet.add("234"); */ Iterator iterator=treeSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } class Person implements Comparable{ private String name; private int id; @Override public int compareTo(Object o) { if(o==this){ return 0; } if(o instanceof Person){ Person p1=(Person) o; int value=Integer.compare(p1.id,this.id);//可不可以直接减去,好像可以 if(value!=0){ return value; } return this.name.compareTo(p1.name);//调用了string里的compareTo方法 } throw new RuntimeException("类型不匹配"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", id=" + id + '}'; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Person(String name, int id) { this.name = name; this.id = id; } } }
4.Map接口·
1.几种常见类(4种):
Hashmap(主要实现类):线程不安全的,效率高;可以添加null的key和value值;底层使用数组+单向链表+红黑树结构存储(jdk8)
Hashtable;(古老实现类):线程安全的,效率低;不可以添加null的key或value值;底层使用数组+单向链表结构存储(jdk8)
LinkedHashMap:是HashMap的子类,在HashMap使用的数据结构增加了一对双向链表,在遍历时按照添加的顺序进行显示。对于频繁的遍历,建议使用这个。
TreeMap:使用红黑树存贮。可以按照添加的key-value中的key元素的指定的属性的大小顺序进行遍历。需要考虑使用①自然排序 ②定制排序。
2.HashMap元素特点
HashMap中的所有的key彼此之间是不可重复的、无序的。所有的key就构成一个Set集合。--->key所在的类要重写hashCode()和equals() HashMap中的所有的value彼此之间是可重复的、无序的。所有的value就构成一个Collection集合。--->value所在的类要重写equals() HashMap中的一个key-value,就构成了一个entry。 HashMap中的所有的entry彼此之间是不可重复的、无序的。所有的entry就构成了一个Set集合。。
3.常用方法:
增:put(Object key,object value),增加键值对
删:remove(object key)//删除key的同时返回value
改:put(Object key,object value)//这个方法其实先是判断Key是否存在,存在则把新的value替换了老的value
查:get(Object key),返回指定键所映射的值,通过key寻找value
长度:size(),集合包含键值对的数量
遍历:Set keySet(),获取所有键的集合。
Collection values(),获取所有值的集合
Set entrySet().获取所有的键值对
containsKey(object Key)contains(object Value)判断是否含键或值,boolean类型的
public void test1(){ Map map=new HashMap(); map.put("hello","ok"); map.put("34","tie"); map.put("34","tied"); map.put("323","tiewhat"); System.out.println(map); System.out.println(map.size());//返回集合元素个数 Object obj= map.remove("34");//返回value System.out.println(obj);//输出value System.out.println(map.get("hello"));//根据key查value System.out.println(map); }
public void test2() { Map map = new HashMap(); map.put(null, null); map.put("34", "tie"); map.put("323", "tiewhat"); map.put("432", "what"); System.out.println(map); Set keyset = map.keySet(); System.out.println(keyset);//输出key Collection values = map.values(); System.out.println(values);//输出value Set enteyset = map.entrySet(); Iterator iterator = enteyset.iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); System.out.println(entry.getKey() + "---->" + entry.getValue()); }//entry是内部接口。 }
4.练习题:
package chapter12.exer4; import java.text.BreakIterator; import java.util.*; public class TestProvince { static Scanner scr = new Scanner(System.in); public static void main(String[] args) { Map model = CityMap.model; Set keySet = model.keySet(); for (Object o : keySet) { System.out.print(o+" "); } System.out.println(); String[] cities; while(true) { System.out.println("请输入你的省份"); String provinces = scr.next(); cities = (String[]) model.get(provinces); if (cities == null || cities.length == 0) { System.out.println("输入的省份有误"); } else { break; } } for (String city : cities) { System.out.print(city+" "); }//输出城市 l:while (true) { System.out.println("请选择你所在的城市!"); String city = scr.next(); for (String s : cities) { if (s.equals(city)) { System.out.println("信息登记结束"); break l; } } System.out.println("城市输入有误!请重新输入!"); } scr.close(); } class CityMap { public static Map model = new HashMap(); static { model.put("北京", new String[]{"北京"}); model.put("辽宁", new String[]{"沈阳", "盘锦", "铁岭", "丹东", "大连", "锦州", "营口"}); model.put("吉林", new String[]{"长春", "延边", "吉林", "白山", "白城", "四平", "松原"}); model.put("河北", new String[]{"承德", "沧州", "邯郸", "邢台", "唐山", "保定", "石家庄"}); model.put("河南", new String[]{"郑州", "许昌", "开封", "洛阳", "商丘", "南阳", "新乡"}); model.put("山东", new String[]{"济南", "青岛", "日照", "临沂", "泰安", "聊城", "德州"}); } } }
5.collections工具类
1.collections是一个操作set,list,map等集合的工具类。
2.区分Collection和Collections:
Collection是集合框架中的用于存贮一个一个元素的接口,又分为List和Set等子接口。
Collections是一个操作set,list,map等集合的工具类,此时的集合框架包括Set,Map,List
3.常用方法:(静态)
sort()排序
swap()交换
shuffle()随机化
reverse()反转
copy()复制
6.复习题:模拟扑克发牌
package chapter12.exer4; import org.junit.Test; import java.util.*; public class GameTest { @Test public void test1(){ String[] num = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; String[] color = {"方片♦","梅花♣","红桃♥","黑桃♠"}; ArrayList poker = new ArrayList(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 13; j++) { poker.add(color[i]+num[j]); } } poker.add("大王"); poker.add("小王"); Collections.shuffle(poker);//已经打乱 List poker1=poker.subList(0,17); List poker2=poker.subList(17,34); List poker3=poker.subList(34,51); List buttom=poker.subList(51,54); HashMap hashMap=new HashMap<>(); hashMap.put("Tom",poker1); hashMap.put("Lili",poker2); hashMap.put("Lucy",poker3); hashMap.put("底牌",buttom); System.out.println(poker); Iterator iterator = hashMap.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry entrymap =(Map.Entry) iterator.next(); System.out.println(entrymap.getKey()); System.out.println(entrymap.getValue()); } } }
完结集合一章,撒花!!知识要有体系
六.泛型
也称类型实参,作用其实就是限制类型。
1.在集合,比较器中的使用
package chapter13.exer1; import org.junit.Test; import java.util.*; public class CollectionMapTest { @Test public void test1(){ } @Test public void test2(){ HashMap<String, Integer> map = new HashMap<>(); map.put("tom",42); map.put("ret",12); map.put("huioie",56); // Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); var entries = map.entrySet(); var iterator=entries.iterator(); while (iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); System.out.println(entry.getValue()+entry.getKey()); } } }
2.自定义泛型
1.自定义泛型方法
public <E> E method(E e){ return null; }
public <E> 返回值类型 方法名(形参列表){}
//定义泛型方法,将E[]数组元素添加到对应类型的ArrayList中,并返回 public <E> ArrayList<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for(E e : arr){ list.add(e); }
return list; }
2.
Arraylist<object>和Arraylist<String>并没有关系。
而SuperA<G>和SuperA<G>有子父类的关系
3.通配符:
-
? 的使用 (重点)
-
以集合为例:可以读取数据、不能写入数据(例外:null)
-
-
? extends A
-
以集合为例:可以读取数据、不能写入数据(例外:null)
-
-
? super A
-
以集合为例:可以读取数据、可以写入A类型或A类型子类的数据(例外:null)
-
七.数据结构与集合源码
八File类与IO流
1.File类
1.构造器 public File(String pathname) :以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。 public File(String parent, String child) :以parent为父路径,child为子路径创建File对象。 public File(File parent, String child) :根据一个父File对象和子文件路径创建File对象
2.IO流
-
IO流的分类
流向的不同:输入流、输出流 处理单位的不同:字节流、字符流 流的角色的不同:节点流、处理流
inputstream:字节输入流。 outputstream:字节输出流
reader:字符输入流。writer:字符输出流。
-
基础IO流的框架: 抽象基类 4个节点流 (也称为文件流) InputStream FileInputStream OutputStream FileOutputStream Reader FileReader Writer FileWriter
说明:本章虽然涉及到的流很多,但是使用流进行数据的读写操作是非常标准和规范的。
-
FileReader \ FileWriter 的使用 3.1 执行步骤: 第1步:创建读取或写出的File类的对象 第2步:创建输入流或输出流 第3步:具体的读入或写出的过程。 读入:read(char[] cbuffer) 写出:write(String str) / write(char[] cbuffer,0,len) 第4步:关闭流资源,避免内存泄漏
3.2 注意点: ① 因为涉及到流资源的关闭操作,所以出现异常的话,需要使用try-catch-finally的方式来处理异常 ② 对于输入流来讲,要求File类的对象对应的物理磁盘上的文件必须存在。否则,会报FileNotFoundException 对于输出流来讲,File类的对象对应的物理磁盘上的文件可以不存在。 > 如果此文件不存在,则在输出的过程中,会自动创建此文件,并写出数据到此文件中。 > 如果此文件存在,使用 FileWriter(File file) 或 FileWriter(File file,false): 输出数据过程中,会新建同名的文件对现有的文件进行覆盖。 FileWriter(File file,true) : 输出数据过程中,会在现有的文件的末尾追加写出内容。
-
FileInputStream \ FileOutputStream 的使用 4.1 执行步骤: 第1步:创建读取或写出的File类的对象 第2步:创建输入流或输出流 第3步:具体的读入或写出的过程。 读入:read(byte[] buffer) 写出:write(byte[] buffer,0,len) 第4步:关闭流资源,避免内存泄漏
4.2 注意点:
在3.2 注意点的基础之上,看其他的注意点。 对于字符流,只能用来操作文本文件,不能用来处理非文本文件的。 对于字节流,通常是用来处理非文本文件的。但是,如果涉及到文本文件的复制操作,也可以使用字节流。
说明: 文本文件:.txt 、.java 、.c、.cpp、.py等 非文本文件:.doc、.xls 、.jpg 、.pdf、.mp3、.mp4、.avi 等
package exer2; import org.junit.Test; import java.io.*; /* * 16点34分begin * 16点42分finish*/ public class IOTest2 { @Test public void test1() throws IOException { FileReader fr = null; FileWriter fw = null; try { File fileScr = new File("hellojava"); File fileDes = new File("hellojava.copy"); fr = new FileReader(fileScr); fw = new FileWriter(fileDes); char[] cbuffer=new char[5]; int len; while((len=fr.read(cbuffer))!=-1){ fw.write(cbuffer,0,len); } System.out.println("success!"); } catch (IOException e) { e.printStackTrace(); } finally { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.缓冲流
-
基础IO流的框架 抽象基类 4个节点流 (也称为文件流) 4个缓冲流(处理流的一种) InputStream FileInputStream BufferedInputStream OutputStream FileOutputStream BufferedOutputStream Reader FileReader BufferedReader Writer FileWriter BufferedWriter
-
缓冲流的作用: 提升文件读写的效率。
3. 4个缓冲流 使用的方法 处理非文本文件的字节流: BufferedInputStream read(byte[] buffer) BufferedOutputStream write(byte[] buffer,0,len) 、flush()
处理文本文件的字符流: BufferedReader read(char[] cBuffer) / String readLine() BufferedWriter write(char[] cBuffer,0,len) / write(String ) 、flush()
-
实现的步骤
第1步:创建File的对象、流的对象(包括文件流、缓冲流)
第2步:使用缓冲流实现 读取数据 或 写出数据的过程(重点) 读取:int read(char[] cbuf/byte[] buffer) : 每次将数据读入到cbuf/buffer数组中,并返回读入到数组中的字符的个数 写出:void write(String str)/write(char[] cbuf):将str或cbuf写出到文件中 void write(byte[] buffer) 将byte[]写出到文件中
第3步:关闭资源
public void test1() throws IOException{ BufferedReader bfr1 = new BufferedReader(new FileReader(new File("hello.txt"))); BufferedWriter bwr1 = new BufferedWriter(new FileWriter(new File("hello_copy.txt"))); char[] cbuffer=new char[1024]; int len; while((len=bfr1.read(cbuffer))!=-1){ for (int i = 0; i <len ; i++) { bwr1.write(cbuffer[i]); } } System.out.println("success"); bfr1.close(); bwr1.close(); }
4.转换流
-
复习
字符编码:字符、字符串、字符数组---> 字节、字节数组(从我们能看得懂的--->我们看不懂的) 字符解码:字节、字节数组 ---> 字符、字符串、字符数组(从我们看不懂的--->我们能看得懂的)
-
如果希望程序在读取文本文件时,不出现乱码,需要注意什么?
解码时使用的字符集必须与当初编码时使用的字符集得相同。
拓展:解码集必须要与编码集兼容。比如:文件编码使用的是GBK,解码时使用的是utf-8。如果文件中只有abc等英文字符,此情况下 也不会出现乱码。因为GBK和utf-8都向下兼容了ASCII (或 ascii)
-
转换流: ① 作用:实现字节与字符之间的转换
② API: InputStreamReader:将一个输入型的字节流转换为输入型的字符流。 OutputStreamWriter:将一个输出型的字符流转换为输出型的字节流。
-
关于字符集的理解 4.1 在存储的文件中的字符: ascii:主要用来存储a、b、c等英文字符和1、2、3、常用的标点符号。每个字符占用1个字节。
iso-8859-1:了解,每个字符占用1个字节。向下兼容ascii。
gbk:用来存储中文简体繁体、a、b、c等英文字符和1、2、3、常用的标点符号等字符。 中文字符使用2个字节存储的。向下兼容ascii,意味着英文字符、1、2、3、标点符号仍使用1个字节。
utf-8:可以用来存储世界范围内主要的语言的所有的字符。使用1-4个不等的字节表示一个字符。 中文字符使用3个字节存储的。向下兼容ascii,意味着英文字符、1、2、3、标点符号仍使用1个字节。
4.2 在内存中的字符:
一个字符(char)占用2个字节。在内存中使用的字符集称为Unicode字符集。
//将gbk的格式转换为utf-8 @Test public void test2() throws IOException { InputStreamReader isr1 = new InputStreamReader(new FileInputStream(new File("dbcp_gbk.txt")),"GBK"); OutputStreamWriter osr1 = new OutputStreamWriter(new FileOutputStream(new File("dbcp_copy_to_utf-8")),"UTF-8"); char[] chars1=new char[1024]; int len; while ((len=isr1.read(chars1))!=-1){ osr1.write(chars1,0,len); } System.out.println("success!"); isr1.close(); osr1.close(); }
5.对象流
-
对象流及其作用 2.1 API: ObjectInputSteam ObjectOutputStream
2.2 作用: 可以读写基本数据类型的变量、引用数据类型的变量。
-
对象的序列化机制是什么 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上, 或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
4.如下两个过程使用的流: 序列化过程:使用ObjectOutputStream流实现。将内存中的Java对象保存在文件中或通过网络传输出去
反序列化过程:使用ObjectInputSteam流实现。将文件中的数据或网络传输过来的数据还原为内存中的Java对象
5.自定义类要想实现序列化机制,需要满足: ① 自定义类需要实现接口:Serializable ② 要求自定义类声明一个全局常量: static final long serialVersionUID = 42234234L; 用来唯一的标识当前的类。 ③ 要求自定义类的各个属性也必须是可序列化的。
对于基本数据类型的属性:默认就是可以序列化的 对于引用数据类型的属性:要求实现Serializable接口
6.注意点: ① 如果不声明全局常量serialVersionUID,系统会自动声明生成一个针对于当前类的serialVersionUID。 如果修改此类的话,会导致serialVersionUID变化,进而导致反序列化时,出现InvalidClassException异常。 ② 类中的属性如果声明为transient或static,则不会实现序列化。
九.网络编程
-
要想实现网络通信,需要解决的三个问题:
-
问题1:如何准确地定位网络上一台或多台主机
-
问题2:如何定位主机上的特定的应用
-
问题3:找到主机后,如何可靠、高效地进行数据传输
-
实现网络传输的三个要素:(对应解决三个问题)
使用IP地址(准确地定位网络上一台或多台主机) 使用端口号(定位主机上的特定的应用) 规范网络通信协议(可靠、高效地进行数据传输)
-
通信要素1:IP地址 3.1 作用 IP地址用来给网络中的一台计算机设备做唯一的编号
3.2 IP地址分类
IP地址分类方式1 IPv4 (占用4个字节) IPv6 (占用16个字节)
IP地址分类方式2 公网地址( 万维网使用)和 私有地址( 局域网使用。以192.168开头)
3.3 本地回路地址:127.0.0.1
3.4 域名:便捷的记录ip地址 www.baidu.com www.atguigu.com www.bilibili.com www.jd.com www.mi.com www.vip.com
-
通信要素2:端口号
可以唯一标识主机中的进程(应用程序) 不同的进程分配不同的端口号 范围:0~65535
-
InetAddress的使用 5.1 作用 InetAddress类的一个实例就代表一个具体的ip地址。**
5.2 实例化方式 InetAddress getByName(String host):获取指定ip对应的InetAddress的实例 InetAddress getLocalHost():获取本地ip对应的InetAddress的实例
5.3 常用方法 getHostName()获取域名 getHostAddress()获取域名的ip地址
-
通信要素3:通信协议 6.1 网络通信协议的目的 为了实现可靠而高效的数据传输。
6.2 网络参考模型 OSI参考模型:将网络分为7层,过于理想化,没有实施起来。 TCP/IP参考模型:将网络分为4层:应用层、传输层、网络层、物理+数据链路层。事实上使用的标准。
注明:网络编程并没有学完,漏了几集,IO流的一些名称也搞得有点迷糊。暂时放下,不死磕,先往后学,到时候遇到问题可以再回来看。
十.反射
1.Class类的理解
Class clazz1 = Person.class; //运行时类 Class clazz2 = String.class; Class clazz3 = User.class; Class clazz4 = Comparable.class;
2.获取Class实例的几种方式
public void test1() throws ClassNotFoundException { //1 调用运行时类的静态属性 Class clazz1= Person1.class; //2调用运行时类的对象的getclass Person1 p1=new Person1(); Class clazz2 = p1.getClass(); //3调用静态方法forName String classname="exer1.Person1"; Class clazz3 = Class.forName(classname); }
3.反射的应用3:调用指定的结构:指定的属性、方法、构造器 3.1 调用指定的属性(步骤) 步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性 步骤2. setAccessible(true):确保此属性是可以访问的 步骤3. 通过Filed类的实例调用get(Object obj) (获取的操作) 或 set(Object obj,Object value) (设置的操作)进行操作。
public void test1() throws Exception { //使用Person类的Class对象创建了一个Person类的实例,并将其赋值给p1变量。 //这句话特别精炼,Class对象就是这里的clazz, // 而clazz.newInstance其实就是创建实例的一种方式,后面只是进行了强制转化 Class clazz = Person.class; //这个方式更常见,Class clazz=Class.forName("exer2.data.Person"); Person p1 = (Person) clazz.newInstance(); //1获取运行时类指定名的属性 Field ageField = clazz.getDeclaredField("age"); //2.确保此属性是可以访问的 ageField.setAccessible(true); //3.获取或设置此属性的值 ageField.set(p1, 4); System.out.println(ageField.get(p1)); }
3.2 调用指定的方法(步骤) 步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法 步骤2. setAccessible(true):确保此方法是可访问的 步骤3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。 invoke()的返回值即为Method对应的方法的返回值 特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
public void test2() throws Exception { Class clazz2 = Person.class; Person p2 = (Person) clazz2.newInstance(); Method show1 = clazz2.getDeclaredMethod("showNation", String.class, int.class); show1.setAccessible(true); Object return1=show1.invoke(p2,"CHN",10); System.out.println(return1); }
3.3 调用指定的构造器(步骤) 步骤1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器 步骤2.setAccessible(true):确保此构造器是可以访问的 步骤3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。
Test public void test3() throws Exception { Class clazz3 = Person.class; //1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器 Constructor constructor = clazz3.getDeclaredConstructor(String.class, int.class); //2.setAccessible(true):确保此构造器是可以访问的 constructor.setAccessible(true); //3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。 Person per = (Person) constructor.newInstance("Tom", 12); System.out.println(per); }
//这三个是要求掌握的。
//部分内容未显示,反射是框架的灵魂,后面学习框架再深度学习也不迟。
十一.JDK新特性
1.lambda表达式
Lambda表达式的格式
-> : lambda操作符或箭头操作符 -> 的左边: lambda形参列表,对应着要重写的接口中的抽象方法的形参列表。 -> 的右边: lambda体,对应着接口的实现类要重写的方法的方法体。
Lambda表达式的本质:
一方面,lambda表达式作为接口的实现类的对象。 ---> "万事万物皆对象" 另一方面,lambda表达式是一个匿名函数。
函数式接口:
什么是函数式接口?为什么需要函数式接口?
如果接口中只声明有一个抽象方法,则此接口就称为函数式接口。
因为只有给函数式接口提供实现类的对象时,我们才可以使用lambda表达式。
结语
我写的这几篇博客都是我学java时的学习笔记。大多数都是自己写的,有一部分是复制粘贴。我发在CSDN上更多的是留个纪念,也为了某一天会用得上翻出来看看。这个真正意义上是写的自己的。也并没有在排版上多费心思,读者看起来会很乱,阅读体验或许不佳。这个我还是很抱歉的。但如果你有幸看到,我觉得你也可以采用这个方式,去做笔记,做笔记的过程一方面可以提高自己的专注度,另一方面就是可以留着以后看。笔记就一个核心要义:自己看得懂就行。
Java是我看B站上的康师傅,讲的很好,全面且细致,我大概学了快一个月。一方面感谢这个时代,这个平台给了我们网上学习的机会,不然光看书去学习,无疑要走很多弯路,最简单的,可能安装个IDEA软件配置一下环境都得搞半天。但这个东西它很难吗?并不是,看了就会,但没人教,很多很简单的东西,可能讲起来就是三分钟,自己摸索可能得半小时。
有点劳累,没什么多说的,有些话光是想想都觉得老生常谈。分享一本书《遥远的救世主》,可遇不可求的一本好书,我前几天刚看完,非常不错。为什么要推荐书呢?因为这本书写的确实很好,值得被阅读,是一本可以让人“开悟”的书。虽只是一家之言,但只要有一个人因为我去读了这本书,而且觉得不错,我觉得我就没有白写这几个字。我还是想起了我高一上学期最后一节政治课,政治老师给班上推荐了几本书,其中就有《未来简史》,或许最终看的人不多,但得益于她的一个”随手之举“,我阅读到了一本好书。我感到很幸运。