本文共 31927 字,大约阅读时间需要 106 分钟。
程序(programm)
概念:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。进程(process)
概念:程序的一次执行过程,或是正在运行的一个程序。 说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域线程(thread)
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。 说明:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。方式一:继承Thread类的方式:
说明两个问题:
问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。 问题二:如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start().方式二:实现Runnable接口的方式:
两种方式的对比:
2. 实现的方式更适合来处理多个线程共享数据的情况。
Thread类中的常用的方法:
线程的优先级:
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5 -->默认优先级
2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只当高优先级的线程执行完以后,低优先级的线程才执行。
线程通信:wait() / notify() / notifyAll() :此三个方法定义在Object类中的。
补充:线程的分类
一种是守护线程,一种是用户线程。图示:
说明:
1.生命周期关注两个概念:状态、相应的方法 2.关注:状态a–>状态b:哪些方法执行了(回调方法) 某个方法主动调用:状态a–>状态b 3.阻塞:临时状态,不可以作为最终状态 死亡:最终状态。1.背景
例子:创建个窗口卖票,总票数为100张.使用实现Runnable接口的方式 *2.Java解决方案:同步机制
在Java中,我们通过同步机制,来解决线程的安全问题。方式一:同步代码块
synchronized(同步监视器){ //需要被同步的代码 }
说明:
1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。 要求:多个线程必须要共用同一把锁。补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。关于同步方法的总结:
方式三:Lock锁 — JDK5.0新增
使用的优先顺序:
Lock —> 同步代码块(已经进入了方法体,分配了相应资源 ) —> 同步方法(在方法体之外) 3.利弊 同步的方式,解决了线程的安全问题。—好处 操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。使用同步机制将单例模式中的懒汉式改写为线程安全的。
class Bank{ private Bank(){ } private static Bank instance = null; public static Bank getInstance(){ //方式一:效率稍差// synchronized (Bank.class) { // if(instance == null){ //// instance = new Bank();// }// return instance;// } //方式二:效率更高 if(instance == null){ synchronized (Bank.class) { if(instance == null){ instance = new Bank(); } } } return instance; }}
面试题:写一个线程安全的单例模式。
饿汉式。 懒汉式:上面提供的。1.死锁的理解:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁2.说明:
public static void main(String[] args) { StringBuffer s1 = new StringBuffer(); StringBuffer s2 = new StringBuffer(); new Thread(){ @Override public void run() { synchronized (s1){ s1.append("a"); s2.append("1"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2){ s1.append("b"); s2.append("2"); System.out.println(s1); System.out.println(s2); } } } }.start(); new Thread(new Runnable() { @Override public void run() { synchronized (s2){ s1.append("c"); s2.append("3"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1){ s1.append("d"); s2.append("4"); System.out.println(s1); System.out.println(s2); } } } }).start();}
1.线程通信涉及到的三个方法:
2.说明:
小结释放锁的操作:
新增方式一:实现Callable接口。 — JDK 5.0新增
//1.创建一个实现Callable的实现类class NumThread implements Callable{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if(i % 2 == 0){ System.out.println(i); sum += i; } } return sum; }}public class ThreadNew { public static void main(String[] args) { //3.创建Callable接口实现类的对象 NumThread numThread = new NumThread(); //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() new Thread(futureTask).start(); try { //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。 Object sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }}
说明:
新增方式二:使用线程池
class NumberThread implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } }}class NumberThread1 implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } }}public class ThreadPool { public static void main(String[] args) { //1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置线程池的属性// System.out.println(service.getClass());// service1.setCorePoolSize(15);// service1.setKeepAliveTime(); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 service.execute(new NumberThread());//适合适用于Runnable service.execute(new NumberThread1());//适合适用于Runnable// service.submit(Callable callable);//适合使用于Callable //3.关闭连接池 service.shutdown(); }}
说明:
面试题:Java中多线程的创建有几种方式?四种。
常用方法:
int length():返回字符串的长度: return value.length char charAt(int index): 返回某索引处的字符return value[index] boolean isEmpty():判断是否是空字符串:return value.length == 0 String toLowerCase():使用默认语言环境,将 String 中的所字符转换为小写 String toUpperCase():使用默认语言环境,将 String 中的所字符转换为大写 String trim():返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj):比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写 String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString):比较两个字符串的大小 String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始 boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引 int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引 int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索注:indexOf和lastIndexOf方法如果未找到都是返回-1
替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所匹配字面值目标序列的子字符串。 String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所匹配给定的正则表达式的子字符串。 String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 匹配: boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。 切片: String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。 String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str) 基本数据类型、包装类 --> String:调用String重载的valueOf(xxx) @Test public void test1(){ String str1 = "123";// int num = (int)str1;//错误的 int num = Integer.parseInt(str1); String str2 = String.valueOf(num);//"123" String str3 = num + ""; System.out.println(str1 == str3); }
String --> char[]:调用 String的toCharArray()char[] --> String:调用 String的构造器@Testpublic void test2(){ String str1 = "abc123"; //题目: a21cb3 char[] charArray = str1.toCharArray(); for (int i = 0; i < charArray.length; i++) { System.out.println(charArray[i]); } char[] arr = new char[]{ 'h','e','l','l','o'}; String str2 = new String(arr); System.out.println(str2);}
编码:String --> byte[]:调用String的getBytes()解码:byte[] --> String:调用String的构造器编码:字符串 -->字节 (看得懂 --->看不懂的二进制数据)解码:编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 ---> 看得懂说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。@Testpublic void test3() throws UnsupportedEncodingException { String str1 = "abc123中国"; byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。 System.out.println(Arrays.toString(bytes)); byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。 System.out.println(Arrays.toString(gbks)); System.out.println("******************"); String str2 = new String(bytes);//使用默认的字符集,进行解码。 System.out.println(str2); String str3 = new String(gbks); System.out.println(str3);//出现乱码。原因:编码集和解码集不一致! String str4 = new String(gbks, "gbk"); System.out.println(str4);//没出现乱码。原因:编码集和解码集一致!}
String -->StringBuffer、StringBuilder:
调用StringBuffer、StringBuilder构造器 StringBuffer、StringBuilder -->String: ①调用String构造器;②StringBuffer、StringBuilder的toString()1.String、StringBuffer、StringBuilder三者的对比
String:不可变的字符序列;底层使用char[]存储 StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储 StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储2.StringBuffer与StringBuilder的内存解析
以StringBuffer为例:String str = new String();//char[] value = new char[0];String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。System.out.println(sb1.length());//sb1.append('a');//value[0] = 'a';sb1.append('b');//value[1] = 'b';StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
//问题1. System.out.println(sb2.length());//3
//问题2. 扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。 默认情况下,扩容为原来容量的2倍 + 2,同时将原数组中的元素复制到新的数组中。指导意义:开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity)
3.对比String、StringBuffer、StringBuilder三者的执行效率
从高到低排列:StringBuilder > StringBuffer > String4.StringBuffer、StringBuilder中的常用方法
增:append(xxx) 删:delete(int start,int end) 改:setCharAt(int n ,char ch) / replace(int start, int end, String str) 查:charAt(int n ) 插:insert(int offset, xxx) 长度:length(); *遍历:for() + charAt() / toString()1.获取系统当前时间:System类中的currentTimeMillis()
long time = System.currentTimeMillis();//返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。//称为时间戳System.out.println(time);
/* java.util.Date类 |---java.sql.Date类 1.两个构造器的使用 >构造器一:Date():创建一个对应当前时间的Date对象 >构造器二:创建指定毫秒数的Date对象 2.两个方法的使用 >toString():显示当前的年、月、日、时、分、秒 >getTime():获取当前Date对象对应的毫秒数。(时间戳) 3. java.sql.Date对应着数据库中的日期类型的变量 >如何实例化 >如何将java.util.Date对象转换为java.sql.Date对象*/ @Test public void test2(){ //构造器一:Date():创建一个对应当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString());//Sat Feb 16 16:35:31 GMT+08:00 2019 System.out.println(date1.getTime());//1550306204104 //构造器二:创建指定毫秒数的Date对象 Date date2 = new Date(155030620410L); System.out.println(date2.toString()); //创建java.sql.Date对象 java.sql.Date date3 = new java.sql.Date(35235325345L); System.out.println(date3);//1971-02-13 //如何将java.util.Date对象转换为java.sql.Date对象 //情况一:// Date date4 = new java.sql.Date(2343243242323L);// java.sql.Date date5 = (java.sql.Date) date4; //情况二: Date date6 = new Date(); java.sql.Date date7 = new java.sql.Date(date6.getTime()); }
2.SimpleDateFormat的实例化:new + 构造器
//*************照指定的方式格式化和解析:调用带参的构造器*****************// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa"); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //格式化 String format1 = sdf1.format(date); System.out.println(format1);//2019-02-18 11:48:27 //解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现), //否则,抛异常 Date date2 = sdf1.parse("2020-02-18 11:48:27"); System.out.println(date2);
小练习:
/* 练习一:字符串"2020-09-08"转换为java.sql.Date 练习二:"天打渔两天晒网" 1990-01-01 xxxx-xx-xx 打渔?晒网? 举例:2020-09-08 ? 总天数 总天数 % 5 == 1,2,3 : 打渔 总天数 % 5 == 4,0 : 晒网 总天数的计算? 方式一:( date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24) + 1 方式二:1990-01-01 --> 2019-12-31 + 2020-01-01 -->2020-09-08 */ @Test public void testExer() throws ParseException { String birth = "2020-09-08"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf1.parse(birth);// System.out.println(date); java.sql.Date birthDate = new java.sql.Date(date.getTime()); System.out.println(birthDate); }
4.Calendar类:日历类、抽象类
//1.实例化 //方式一:创建其子类(GregorianCalendar的对象 //方式二:调用其静态方法getInstance() Calendar calendar = Calendar.getInstance();// System.out.println(calendar.getClass()); //2.常用方法 //get() int days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); //set() //calendar可变性 calendar.set(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); //add() calendar.add(Calendar.DAY_OF_MONTH,-3); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); //getTime():日历类---> Date Date date = calendar.getTime(); System.out.println(date); //setTime():Date ---> 日历类 Date date1 = new Date(); calendar.setTime(date1); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);
Java中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 的
但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。 如何实现?使用两个接口中的任何一个:Comparable 或 Comparator说明
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。 2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列 3. 重写compareTo(obj)的规则: 如果当前对象this大于形参对象obj,则返回正整数, 如果当前对象this小于形参对象obj,则返回负整数, 如果当前对象this等于形参对象obj,则返回零。 4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序public class Goods implements Comparable{ private String name; private double price; //指明商品比较大小的方式:照价格从低到高排序,再照产品名称从高到低排序 @Override public int compareTo(Object o) { // System.out.println("**************"); if(o instanceof Goods){ Goods goods = (Goods)o; //方式一: if(this.price > goods.price){ return 1; }else if(this.price < goods.price){ return -1; }else{ // return 0; return -this.name.compareTo(goods.name); } //方式二:// return Double.compare(this.price,goods.price); }// return 0; throw new RuntimeException("传入的数据类型不一致!"); }// getter、setter、toString()、构造器:省略}
3.1 说明
1.背景: 当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序 2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小: (可使用系统自动生成的equals方法修改一下) 如果方法返回正整数,则表示o1大于o2; 如果返回0,表示相等; 返回负整数,表示o1小于o2。Comparator com = new Comparator() { //指明商品比较大小的方式:照产品名称从低到高排序,再照价格从高到低排序 @Override public int compare(Object o1, Object o2) { if(o1 instanceof Goods && o2 instanceof Goods){ Goods g1 = (Goods)o1; Goods g2 = (Goods)o2; if(g1.getName().equals(g2.getName())){ return -Double.compare(g1.getPrice(),g2.getPrice()); }else{ return g1.getName().compareTo(g2.getName()); } } throw new RuntimeException("输入的数据类型不一致"); }}
使用:
Arrays.sort(goods,com); Collections.sort(coll,com); new TreeSet(com);集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)增:add(Object obj)
删:remove(int index) / remove(Object obj) 改:set(int index, Object ele) 查:get(int index) 插:add(int index, Object ele) 长度:size() 遍历:① Iterator迭代器方式 ② 增强for循环 ③ 普通的循环|----Collection接口:单列集合,用来存储一个一个的对象
|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
|----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序 sort(List):根据元素的自然顺序对指定 List 集合元素升序排序 sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换 Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素 Object min(Collection) Object min(Collection,Comparator) int frequency(Collection,Object):返回指定集合中指定元素的出现次数 void copy(List dest,List src):将src中的内容复制到dest中 boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所旧值1.泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返 回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、 创建对象时确定(即传入实际的类型参数,也称为类型实参)。2.泛型的引入背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型。@Test public void test1(){ ArrayList list = new ArrayList(); //需求:存放学生的成绩 list.add(78); list.add(76); list.add(89); list.add(88); //问题一:类型不安全// list.add("Tom"); for(Object score : list){ //问题二:强转时,可能出现ClassCastException int stuScore = (Integer) score; System.out.println(stuScore); } }
图示:
@Test public void test2(){ ArrayListlist = new ArrayList (); list.add(78); list.add(87); list.add(99); list.add(65); //编译时,就会进行类型检查,保证数据的安全// list.add("Tom"); //方式一:// for(Integer score : list){ // //避免了强转操作// int stuScore = score;//// System.out.println(stuScore);//// } //方式二: Iterator iterator = list.iterator(); while(iterator.hasNext()){ int stuScore = iterator.next(); System.out.println(stuScore); } }
图示:
//在集合中使用泛型的情况:以HashMap为例 @Test public void test3(){ // Mapmap = new HashMap (); //jdk7新特性:类型推断 Map map = new HashMap<>(); map.put("Tom",87); map.put("Jerry",87); map.put("Jack",67);// map.put(123,"ABC"); //泛型的嵌套 Set > entry = map.entrySet(); Iterator > iterator = entry.iterator(); while(iterator.hasNext()){ Map.Entry e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----" + value); } }
1.举例:
【Order.java】public class Order{ String orderName; int orderId; //类的内部结构就可以使用类的泛型 T orderT; public Order(){ //编译不通过// T[] arr = new T[10]; //编译通过 T[] arr = (T[]) new Object[10]; } public Order(String orderName,int orderId,T orderT){ this.orderName = orderName; this.orderId = orderId; this.orderT = orderT; } //如下的个方法都不是泛型方法 public T getOrderT(){ return orderT; } public void setOrderT(T orderT){ this.orderT = orderT; } @Override public String toString() { return "Order{" + "orderName='" + orderName + '\'' + ", orderId=" + orderId + ", orderT=" + orderT + '}'; } //静态方法中不能使用类的泛型。// public static void show(T orderT){ // System.out.println(orderT);// } public void show(){ //编译不通过// try{ ////// }catch(T t){ //// } } //泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没任何关系。 //换句话说,泛型方法所属的类是不是泛型类都没关系。 //泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。 public static List copyFromArrayToList(E[] arr){ ArrayList list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list; }}
【SubOrder.java】
public class SubOrder extends Order{ //SubOrder:不是泛型类 public static List copyFromArrayToList(E[] arr){ ArrayList list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list; }}//实例化时,如下的代码是错误的SubOrder o = new SubOrder<>();【SubOrder1.java】public class SubOrder1 extends Order { //SubOrder1 :仍然是泛型类}
【测试】
@Test public void test1(){ //如果定义了泛型类,实例化没指明类的泛型,则认为此泛型类型为Object类型 //要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。 Order order = new Order(); order.setOrderT(123); order.setOrderT("ABC"); //建议:实例化时指明类的泛型 Orderorder1 = new Order ("orderAA",1001,"order:AA"); order1.setOrderT("AA:hello"); } @Test public void test2(){ SubOrder sub1 = new SubOrder(); //由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。 sub1.setOrderT(1122); SubOrder1 sub2 = new SubOrder1<>(); sub2.setOrderT("order2..."); } @Test public void test3(){ ArrayList list1 = null; ArrayList list2 = new ArrayList (); //泛型不同的引用不能相互赋值。// list1 = list2; Person p1 = null; Person p2 = null; p1 = p2; } //测试泛型方法 @Test public void test4(){ Order order = new Order<>(); Integer[] arr = new Integer[]{ 1,2,3,4}; //泛型方法在调用时,指明泛型参数的类型。 List list = order.copyFromArrayToList(arr); System.out.println(list); }
2.注意点:
3.应用场景举例:
【DAO.java】:定义了操作数据库中的表的通用操作。 ORM思想(数据库中的表和Java中的类对应)public class DAO{ //表的共性操作的DAO //添加一条记录 public void add(T t){ } //删除一条记录 public boolean remove(int index){ return false; } //修改一条记录 public void update(int index,T t){ } //查询一条记录 public T getIndex(int index){ return null; } //查询多条记录 public List getForList(int index){ return null; } //泛型方法 //举例:获取表中一共有多少条记录?获取最大的员工入职时间? public E getValue(){ return null; }}【CustomerDAO.java】:public class CustomerDAO extends DAO { //只能操作某一个表的DAO}【StudentDAO.java】:public class StudentDAO extends DAO { //只能操作某一个表的DAO}
1.File类的理解
2.1 常用构造器
File(String filePath) File(String parentPath,String childPath) File(File parentFile,String childPath)2.2 路径的分类
相对路径:相较于某个路径下,指明的路径。 绝对路径:包含盘符在内的文件或文件目录的路径说明:
IDEA中: 如果大家开发使用JUnit中的单元测试方法测试,相对路径即为当前Module下。 如果大家使用main()测试,相对路径即为当前的Project下。 Eclipse中: 不管使用单元测试方法还是使用main()测试,相对路径都是当前的Project下。2.3 路径分隔符
windows和DOS系统默认使用“\”来表示 UNIX和URL使用“/”来表示① 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
② 创建相应的输入流,将File类的对象作为参数,传入流的构造器中 ③ 具体的读入过程: 创建相应的byte[] 或 char[]。 ④ 关闭流资源 说明:程序中出现的异常需要使用try-catch-finally处理。① 创建File类的对象,指明写出的数据的位置。(不要求此文件一定要存在)
② 创建相应的输出流,将File类的对象作为参数,传入流的构造器中 ③ 具体的写出过程: write(char[]/byte[] buffer,0,len) ④ 关闭流资源 说明:程序中出现的异常需要使用try-catch-finally处理。作用:提供流的读取、写入的速度
提高读写速度的原因:内部提供了一个缓冲区。默认情况下是8kb3.1 使用BufferedInputStream和BufferedOutputStream:处理非文本文件
//实现文件复制的方法 public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.// fos.close();// fis.close(); } }
@Test public void testBufferedReaderBufferedWriter(){ BufferedReader br = null; BufferedWriter bw = null; try { //创建文件和相应的流 br = new BufferedReader(new FileReader(new File("dbcp.txt"))); bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"))); //读写操作 //方式一:使用char[]数组// char[] cbuf = new char[1024];// int len;// while((len = br.read(cbuf)) != -1){ // bw.write(cbuf,0,len);// // bw.flush();// } //方式二:使用String String data; while((data = br.readLine()) != null){ //方法一:// bw.write(data + "\n");//data中不包含换行符 //方法二: bw.write(data);//data中不包含换行符 bw.newLine();//提供换行的操作 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
学艺不精 只会用
参考代码package com.xzh.web;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;/** * 这个类继承了HttpServlet并且重写了doPost方法 * 方便以后其他继承这个代码用反射来调用其对应的方法 * * @Author Lin * @CreateTime 2021/5/10 9:45 */public abstract class BaseServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解决请求和转发乱码 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); //得到网页的信息 判断该页面是什么页面 //<%--action后面的值表示要调用的servlet的方法--%> String action = request.getParameter("action"); try { //通过反射调用方法 getDeclaredMethod(方法名,参数类型 ...args){} Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); //调用方法 并传入参数 method.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); }}
主要为这里
//<%--action后面的值表示要调用的servlet的方法--%> String action = request.getParameter("action"); try { //通过反射调用方法 getDeclaredMethod(方法名,参数类型 ...args){} Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); //调用方法 并传入参数 method.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }
转载地址:http://emymz.baihongyu.com/