运算符 位运算符
运算符
运算
范例
<<
左移,在一定范围内,每向左移一位,相当于 *2
3 << 2 = 3 x 2 x 2 = 12
>>
右移,在一定范围内,每向右移一位,相当于 /2
3 >> 1 = 3 / 2 = 1
>>>
无符号右移
3 >>> 1 = 3 /2 = 1
&
与运算
6 & 3 = 2
|
或运算
6 | 3 = 7
^
异或运算
6 ^ 3 = 5
~
取反运算
~6 = -7
1 2 3 4 5 6 7 << 空位补0,被移除的最高位丢弃,空缺位补0 << 被移位的二进制最高位是0,右移后,空缺位补0,最高位是1,空缺位补1 >>> 被移位二进制最高位无论是0还是1,空缺位都补0 & 二进制位进行与运算,只有1&1时的结果是1,其他都是0 | 二进制位进行或运算,只有0|0时的结果是0,其他都是1 ^ 相同二进制位进行^运算,结果是0;1^1=0,0^0=0,不用的二进制位^运算的结果是1;1^0=1,0^1=1 ~ 正数取反,各二进制码按补码各位取反;负数取反,各二进制码按补码各位取反
面向对象
面向对象的核心概念:类和对象
类是对一类事物的描述,是抽象的,概念上的定义
对象是实际存在的该类事物的每个个体,因而也称为实例。
对象创建和使用:内存解析
==堆==( Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
通常所说的栈( Stack),是指虚拟机==栈==。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型( boolean、 byte、char 、 short 、 int 、 float 、 long 、double ) 、对象引用( reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。
方法区( Method Area),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法的重载
重载的概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数、参数类型或顺序不同即可
重载的特点:与返回值类型无关,只看参数列表,且参数列表必须不同。调用时根据方法参数列表的不同来区别。
可变个数的形参的方法
可变个数的形参必须声明在末尾,一个方法中只能声明一个可变形参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MethodArgsTest { public static void main (String[] args) { MethodArgsTest.show(); MethodArgsTest.show("A" ,"B" ); } public static void show (String ... strs) { for (String str : strs) { System.out.println(str); } } }
方法参数的值传递机制
Java中的参数如何传入方法呢?
Java里方法的参数传递方式只有一种,值传递。即将实际参数的值得副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
权限修饰符 封装性的体现需要权限修饰符来配合。private,缺省(default),protected,public
修饰符
类内部
同一个包
不同包子类
同一个工程
private
√
缺省
√
√
protected
√
√
√
public
√
√
√
√
this关键字
在Java中,this关键字比较难理解,它的作用和词义很接近。
它在方法内部使用,即用这个方法所属对象的引用
它在构造器内部使用,表示该构造器正在初始化的变量
this表示==当前对象==,可以调用类的属性、方法和构造器
supper关键字
supper可以理解为:父类的
supper可以用来调用父类的属性,构造器和方法
static关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,==某些特定的数据在内存空间里只有一份==。
静态变量(类变量):我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的。
静态变量和实例变量的内存解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Chinese { String name; String age; static String nation; } class StaticTest { public static void main (String[] args) { Chinese.nation = "中国" ; Chinese c1 = new Chinese (); c1.name = "姚明" ; c1.age = "40" ; c1.nation = "CHN" ; Chinese c2 = new Chinese (); c1.name = "马龙" ; c1.age = "30" ; c1.nation = "CHINA" ; } }
单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Blank { private Blank () {} private static Blank instance = new Blank (); public static Blank getInstance () { return instance; } } class Blank1 { private Blank1 () {} private static Blank1 instance = null ; public static Blank1 getInstance () { if (instance == null ) return new Blank1 (); return instance; } }
区分饿汉式和懒汉式
饿汉式
坏处:对象加载时间过长
好处:线程安全
懒汉式
好处:延迟对象的创建
坏处:目前写法线程不安全
代码块 1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { private String name; private String age; public Person () {} void show () {} { } }
代码块的作用:
用来初始化类、对象
只能使用static来修饰。因此分为静态代码块和非静态代码块
静态代码块随着类的加载而执行,只执行一次
非静态代码块随着对象的创建而执行,每创建一次对象执行一次
final关键字 final表示最终的,可以用来修饰类,方法,变量
final修饰类,不能被继承
final修饰方法,不可重写
final修饰变量,不可修改,此时的”变量”称为常量,可以考虑的赋值方式显示赋值,代码块赋值,构造器赋值。除此之外,final还可以修饰局部变量
static fianl 修饰属性:全局常量
抽象类和抽象方法 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计的非常重写,以至于它没有具体的实例,这样的类叫做抽象类。
==abstract==表示“抽象的”,可以用来修饰类和方法。
abstract修饰类
abstract修饰的类不能实例化
抽象类中一定有构造器
开发中,都会提供抽象类的子类,让子类实例化,完成相关操作
abstract修饰方法
抽象方法只有声明,没有方法体。
包含抽象方法的类一定是一个抽象类。反之,抽象类中可以没有抽象方法
若子类重写了父类中的所有抽象方法后,子类才可以实例化。若没有重写父类(包括间接父类)中所有的抽象方法,则子类也是一个抽象类。
abstract使用的注意点:
abstract不能用来修饰:属性、构造器等结构
abstract不能用来修饰私有方法、静态方法、final的方法、final的类
模板方法的设计模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class TemplateTest { public static void main (String[] args) { SubTemplate subTemplate = new SubTemplate (); subTemplate.spendTime(); } } abstract class Template { public void spendTime () { long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); System.out.println("所需要的时间为:" +(end-start)); } public abstract void code () ; } class SubTemplate extends Template { @Override public void code () { for (int i = 0 ; i < 1000 ; i++) { boolean isFlag = true ; for (int j = 2 ;j<Math.sqrt(i);j++){ if (i % j == 0 ){ isFlag = false ; break ; } } if (isFlag){ System.out.println(i); } } } }
例子:
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月某个Employee对象的生日,则该员工工资增加100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class MyDate { private int year; private int month; private int day; public MyDate () {}; public MyDate (int year, int month, int day) { this .year = year; this .month = month; this .day = day; } public int getYear () { return year; } public void setYear (int year) { this .year = year; } public int getMonth () { return month; } public void setMonth (int month) { this .month = month; } public int getDay () { return day; } public void setDay (int day) { this .day = day; } @Override public String toString () { return year + "年" + month + "月" + "日" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public abstract class Employee { private String name; private int number; private MyDate myDate; public abstract double earnings () ; public Employee (String name, int number, MyDate myDate) { this .name = name; this .number = number; this .myDate = myDate; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getNumber () { return number; } public void setNumber (int number) { this .number = number; } public MyDate getMyDate () { return myDate; } public void setMyDate (MyDate myDate) { this .myDate = myDate; } @Override public String toString () { return "name='" + name + ", number=" + number +", myDate=" + myDate; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class SalariedEmployee extends Employee { private double monthlySalary; public SalariedEmployee (String name, int number, MyDate birthday) { super (name,number,birthday); } public SalariedEmployee (String name, int number, MyDate birthday,double monthlySalary) { super (name,number,birthday); this .monthlySalary = monthlySalary; } public double getMonthlySalary () { return monthlySalary; } public void setMonthlySalary (double monthlySalary) { this .monthlySalary = monthlySalary; } @Override public double earnings () { return monthlySalary; } @Override public String toString () { return "SalariedEmployee[" +super .toString()+"]" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class HourlyEmployee extends Employee { private int wage; private int hour; public HourlyEmployee (String name, int number, MyDate myDate) { super (name, number, myDate); } public HourlyEmployee (String name, int number, MyDate myDate,int wage,int hour) { super (name, number, myDate); this .hour = hour; this .wage = wage; } @Override public double earnings () { return wage * hour; } @Override public String toString () { return "HourlyEmployee[" +super .toString()+"]" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class PayrollSystem { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入当前月份" ); int month = sc.nextInt(); Employee[] emp = new Employee [2 ]; emp[0 ] = new SalariedEmployee ("马森" ,1002 ,new MyDate (1992 ,2 ,28 ),10000 ); emp[1 ] = new HourlyEmployee ("cjz" ,1007 ,new MyDate (2001 ,9 ,26 ),60 ,240 ); for (Employee employee : emp) { double salay = employee.earnings(); System.out.println(employee); if ((month == employee.getMyDate().getMonth())){ System.out.println("生日快乐,工资+100" ); salay += 100 ; } System.out.println("月工资为:" + salay); } } }
interface关键字 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个“是不是”的关系,而接口实现则是“能不能”的关系。
接口的使用
接口的应用:静态代理模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class NetWorkTest { public static void main (String[] args) { Server server = new Server (); ProxyServer proxyServer = new ProxyServer (server); proxyServer.browse(); } } interface NetWork { void browse () ; } class Server implements NetWork { @Override public void browse () { System.out.println("真实的服务器网络" ); } } class ProxyServer implements NetWork { private NetWork work; public ProxyServer (NetWork work) { this .work = work; } public void check () { System.out.println("联网之前的检查工作" ); } @Override public void browse () { check(); work.browse(); } }
接口中还可以定义静态方法和默认方法
1 2 3 4 5 6 7 8 9 10 interface CompareA { public static void method1 () { System.out.println("11" ); } public default void method2 () { System.out.println("北京" ); } }
内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class InnerClassTest { public static void main (String[] args) { Person.Bird bird = new Person .Bird(); bird.Fay(); Person p = new Person (); Person.Dog dog = p.new Dog (); dog.show(); } } class Person { String name; int age; public void method1 () {} class Dog { String name; public void show () { Person.this .method1(); } } static class Bird { String name; public void Fay () { } } }
异常 异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
Java程序在执行过程中所发生的异常时间分为两类:
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误,资源耗尽等严重情况。StackOverflowError栈溢出,OutOfMemoryError堆溢出。
Exception:其他因编程错误或偶然的外在因素导致一般性问题,可以使用针对性的代码进行处理。
对于这些错误,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方法是由程序在编写程序时,就考虑到错误的检测、错误信息的提升,以及错误的处理。
捕获错误最理想是在编译期间,但有的错误只有在允许时才会发生。
异常分为编译时异常和允许时异常
编译时异常
运行时异常
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组角标越界
ClassCaseException 类型转换异常
NumberFormatException 格式化异常
InputMismatchException 输入不匹配异常
…
自定义异常类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MyException extends RuntimeException { static final long serialVersionUID = 3748921734890127847L ; public MyException () { } public MyException (String message) { super (message); } }
多线程 基本概念:程序,进程,线程
程序
是为了特定任务、用某种语言编写的一组指令的集合。是指一段静态的代码,静态对象
进程
是程序的一次执行过程,或是正在允许的一个程序。是一个动态的过程:有它自身的产生和消亡的过程。—生命周期
进程做为资源分配的单位,系统在允许时会没个进程分配不同的内存区域
线程
进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进行同一时间并行多个线程,就是支持多线程的。
使用多线程的优点
提高应用程序的响应。对应图形化界面更有意义,可增强用户体验
提高计算机系统CPU的利用率
改善程序结构。将即长又复杂的进程分为多个线程,独立运行,利于理解和修改
何时需要多线程
程序需要同时执行两个或多个任务
程序需要实现一些需要等待的任务,如用户输入,文件读写操作,网络操作、等等。
需要一些后台运行的程序时
并行和并发
单核CPU下,线程还是串行执行的。操作系统有一个组件叫做任务调度器,将CPU的时间片分给不同的线程使用。
一般将这种线程轮流使用CPU的作法称为并发 。concurrent。
多核CPU下,每个核都可以调度运行线程,这时候线程是可以并行的。
线程的创建和使用 方式一:继承Thread类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class MyThread extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { if (i % 2 == 0 ){ System.out.println(i); } } } } public class ThreadTest { public static void main (String[] args) { MyThread myThread = new MyThread (); myThread.start(); } }
方式二:实现Runnable接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class RunnableTest implements Runnable { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { if (i % 2 == 0 ){ System.out.println(i); } } } } public class MyRunnable { public static void main (String[] args) { RunnableTest runnableTest = new RunnableTest (); Thread t1 = new Thread (runnableTest); Thread t2 = new Thread (runnableTest); t1.start(); t2.start(); } }
比较创建线程的两种方式
开发中优先选择:实现Runnable接口
实现的方式没有类的单继承的局限性
实现的方式更适合来处理多个线程有共享数据的情况
Thread实现了Runnable接口
JDK5.0新增创建线程方式
方式三:实现Callable接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class NewThreadTest implements Callable <Integer>{ @Override public Integer call () throws Exception { int sum = 0 ; for (int i = 0 ; i < 100 ; i++) { if (i % 2 == 0 ){ System.out.println(i); sum += i; } } return sum; } } public class CallableTest { public static void main (String[] args) { NewThreadTest newThreadTest = new NewThreadTest (); FutureTask<Integer> futureTask = new FutureTask <Integer>(newThreadTest); new Thread (futureTask).start(); try { Integer o = futureTask.get(); System.out.println("总和为:" +o); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
与Runnable接口相比
相比run()方法可以有返回值
方法可以抛出异常
支持泛型返回值
需要借助FutureTask类,比如获取返回结果
方式四:使用线程池创建线程 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建和销毁,实现重复利用。
好处:
提高响应速度(减少创建新线程的时间)
减低资源消耗(重复利用线程池中的线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会停止
Thread类的常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void start () ;run(); String getName () ; void setName () ;static Thread currentThread () ;yield ();join(); join(long n); sleep(long millitime); isAlive(); getStatus(); interrup(); isInterrupted(); interrupted(); isAlive();
线程的调度
1 2 3 4 getPriority(); setPriority(int newPriority);
说明
高优先级的线程要比低优先级线程的执行权,
线程的生命周期 JDK中用Thread.State类定义了线程的几种状态 要想实现多线程,必须在主线程中创建新的线程对象.JAVA语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:
新建:当一个线程类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被启动()后,将进入线程队列等待cpu时间片,此时它已具备了运行的条件,只是没分配到cpu资源
运行:当就绪的线程被调度并获得cpu资源时,便进入运行状态,运行()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出cpu并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
线程的同步 方式一:同步代码块
1 2 3 synchronized (同步监视器){ }
说明
操作共享数据的代码,极为需要被同步的代码
共享数据:多个线程共同操作的变量
同步监视器,俗称:锁。任何一个类的对象都可以充当锁
要求多个线程必须共用同一把锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private Integer ticket = 100 ; Object obj = new Object (); @Override public void run () { while (true ){ synchronized (obj){ if (ticket > 1 ){ try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } ticket --; System.out.println(Thread.currentThread().getName()+"抢到票,票还剩下" +ticket); }else { break ; } } } } } class MyRunnable { public static void main (String[] args) { RunnableTest runnableTest = new RunnableTest (); Thread t1 = new Thread (runnableTest); Thread t2 = new Thread (runnableTest); t1.setName("窗口1" ); t2.setName("窗口2" ); t1.start(); t2.start(); } }
方式二:同步方法
1 2 3 private synchronized void show () { }
同步方法仍然设计到同步监视器,只是不需要我们显式声明
非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class RunnableTest implements Runnable { private Integer ticket = 100 ; @Override public void run () { while (true ) { show(); if (ticket.equals(0 )){ break ; } } } private synchronized void show () { if (ticket > 0 ){ try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到票,票还剩下" +ticket); ticket--; }else { return ; } } } class MyRunnable { public static void main (String[] args) { RunnableTest runnableTest = new RunnableTest (); Thread t1 = new Thread (runnableTest); Thread t2 = new Thread (runnableTest); t1.setName("窗口1" ); t2.setName("窗口2" ); t1.start(); t2.start(); } }
线程安全的懒汉式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Blank1 { private Blank1 () {} private static Blank1 instance = null ; public static synchronized Blank1 getInstance () { if (instance == null ) return new Blank1 (); return instance; } public static synchronized Blank1 getInstance () { if (instance == null ){ synchronized (Blank1.class){ if (instance == null ) return new Blank1 (); return instance; } } } }
死锁
不同的线程分别在占用对方需要的同步资源不放弃,都在等对方放弃需要的同步资源,就形成了线程的死锁
出现死锁后不会出现一次,不会出现提升,只是所有的线程都处于堵塞状态,无法继续
解决方法
专门的算法、原则
尽量减少同步资源的定义
尽量避免嵌套同步
Lock锁 解决线程安全问题的方式三:Lock锁 —JDK5.0新增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class RunnableTest implements Runnable { private Integer ticket = 100 ; private ReentrantLock lock = new ReentrantLock (); @Override public void run () { while (true ) { try { lock.lock(); if (ticket > 1 ) { try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } ticket--; System.out.println(Thread.currentThread().getName() + "抢到票,票还剩下" + ticket); } else { break ; } } finally { lock.unlock(); } } } } class MyRunnable { public static void main (String[] args) { RunnableTest runnableTest = new RunnableTest (); Thread t1 = new Thread (runnableTest); Thread t2 = new Thread (runnableTest); t1.setName("窗口1" ); t2.setName("窗口2" ); t1.start(); t2.start(); } }
synchronized和Lock锁的异同?
相同:都可以解决多线程的安全问题
不同:synchronized机制在执行玩相应的同步代码以后,自动释放同步监视器,Lock锁需要手动开启,手动关闭
线程的通信 1 2 3 4 5 6 wait(); notify(); notifyAll();
wait()、notify()、notifyAll()必须使用在同步代码块或同步方法中
三个方法调用者必须是同步代码块或同步方法中的同步监视器
sleep()方法和wait()方法的区别?
相同: 一旦执行此方法,都可以使得当前线程进入堵塞状态
不同:
两个方法声明的位置不同:sleep()在Thread类中,wait()声明在Object类中
调用的要求不同:sleep()可以在任何场景中使用,wait()必须在同步代码块或同步方法中使用
关于是否释放同步监视器:sleep()不会释放同步监视器,wait()会释放同步监视器
生产者和消费者的问题 生产者(Productor)将产品交给店员(CLerk),而消费者(Customer)从店员处产品,店员一次只能持有固定数量的产品(比如:20)。如果生产者试图生产更多的产品,店员叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来联走产品.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class Clerk { private Integer productCount = 0 ; public synchronized void producerClerk () { if (productCount < 20 ){ System.out.println(Thread.currentThread().getName()+"开始生产第" +(++productCount)+"个产品" ); notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void consumerClerk () { if (productCount > 0 ){ System.out.println(Thread.currentThread().getName()+"开始消费第" +(productCount--)+"个产品" ); notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer implements Runnable { private Clerk clerk; public Producer (Clerk clerk) { this .clerk = clerk; } @Override public void run () { System.out.println(Thread.currentThread().getName()+"开始生产产品...." ); while (true ){ try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } clerk.producerClerk(); } } } class Consumer implements Runnable { private Clerk clerk; public Consumer (Clerk clerk) { this .clerk = clerk; } @Override public void run () { System.out.println(Thread.currentThread().getName()+"开始消费产品...." ); while (true ){ try { Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consumerClerk(); } } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Clerk clerk = new Clerk (); Producer producer = new Producer (clerk); Consumer consumer = new Consumer (clerk); Thread producerThread = new Thread (producer); Thread consumerThread = new Thread (consumer); Thread consumerThread2 = new Thread (consumer); producerThread.setName("生产者" ); consumerThread.setName("消费者1" ); consumerThread2.setName("消费者2" ); producerThread.start(); consumerThread.start(); consumerThread2.start(); } }
涉及线程池的API JDK5.0提供了线程池相关API:==ExecutorService==和==Executors==
常用类 String类
String类:代表字符串。Java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现。底层使用char[] value存储。
String 是一个final类,代表不可变的字符序列
String 实现了Serializable接口:代表字符串是支持序列化的
实现了Comparable接口:表示String可以比较大小
演示String的不可变性
1 2 3 4 5 6 7 8 9 10 11 @Test public void test1 () { String s1 = "abc" ; String s2 = "abc" ; System.out.println(s1 == s2); s1 += "def" ; System.out.println(s1==s2); String replace = s1.replace("a" , "g" ); System.out.println(s1); System.out.println(replace); }
不可变的体现:
当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
当对现有的字符串进行链接操作时,也需要重写指定内存区域赋值,不能使用原有的value进行赋值
当调用S提让的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值。
通过字面量方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
字符串常量池中是不会存储相同内存的字符串
String对象的创建 1 2 3 4 5 6 7 8 String str = "hello" ;String str = new String ();String str = new String (String original);String str = new String (char [] a);String str = new String (char [] a,int startIndex,int count);
1 2 3 4 5 6 7 8 9 10 11 @Test public void test2 () { String s1 = "JavaSE" ; String s2 = "JavaSE" ; String s3 = new String ("JavaSE" ); String s4 = new String ("JavaSE" ); System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s1 == s4); System.out.println(s3 == s4); }
面试题:String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中的new结构,一个是常量池中的数据”abc”。
Stirng 的常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 int length () ;char charAt (int index) ;boolean isEmpty () ;String toLowerCase () ; String toUpperCase () ; String trim () ; boolean equals (Object obj) ;boolean equalslgnoreCase (String anotherString) ;String concat (String str) ; int compareTo (String anotherString) ;String substring (int beginIndex) ; String subString (int beginIndex,int endIndex) ; boolean endsWith (String suffix) ;boolean startsWith (String prefix) ;boolean startsWith (String prefix,int toffset) ;boolean contains (CharSequence s) ;int indexOf (String str) ;int indexOf (String str,int fromIndex) ;int lastIndexOf (String str) ;int lastIndexOf (String str,int fromIndex) ;String replace (char oldChar,char newChar) ; String replace (CharSequence target,CharSequence replacement) ; String replaceAll (String regex,String replacement) ; String replaceAll (String regex,String replacement) ; boolean matches (String regex) ;String[] split(Stirng regex); String[] split(Stirng regex,int limit);
String 和其他结构的转换 String –> 基本数据类型,调用包装类的parseXxx()方法
基本数据类型 –> Stirng,调用String.valueOf()方法。
1 2 3 4 5 6 7 @Test public void test3 () { String str = "123" ; int num = Integer.parseInt(str); String s = String.valueOf(num); }
Stirng –> char[],调用Stirng的toCharArray()方法
char[] – > String,调用Stirng的构造器
1 2 3 4 5 6 @Test public void test4 () { String str = "123" ; char [] value = str.toCharArray(); String s = new String (value); }
Stirng –> byte[],调用String的getBytes();
byte[] –> String,调用String的构造器
1 2 3 4 5 6 @Test public void test5 () { String str = "123" ; byte [] bytes = str.getBytes(); String s = new String (bytes); }
StirngBuffer和StringBuilder
String、StringBuffer和StringBuilder三者的异同
String:不可变的字符序列;底层用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率偏低;底层用char[]存储
StringBuilder:可变的字符序列;线程不安全的,效率高,JDK1.5新增;底层用char[]存储
效率对比:StringBuilder > StringBuffer > String
==源码分析==
1 2 3 4 String str = new String ();String str = new String ("abc" );StringBuffer sb1 = new StringBuffer ();StringBuffer sb1 = new StringBuffer ("abc" );
扩容问题:如果要添加的数据底层数组盛不下了,那就扩容底层的数组。
1 2 3 4 5 6 7 8 9 10 private int newCapacity (int minCapacity) { int newCapacity = (value.length << 1 ) + 2 ; if (newCapacity - minCapacity < 0 ) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0 ) ? hugeCapacity(minCapacity) : newCapacity; }
默认情况下扩容为原来容量的2倍+2,同时将原有数组中内容复制到新的数组中
==常用方法==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 StringBuffer append (Xxx) ; StringBuffer delete (int start,int end) ; StringBuffer replace (int start,int end,String str) ; StringBuffer insert (int offset,String str) ; StringBuffer reverse () ; int indexOf (String str) ;String substring (int start,int end) ; int length () ;char charAt (int n) ;void setCharAt (int n,char ch) ;
常用类String算法题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public String getMaxString (String str1,String str2) { if (str1 != null && str2!= null ){ String maxString = (str1.length() >= str2.length()) ? str1 : str2; String minString = (str1.length() < str2.length()) ? str1 : str2; int minSize = minString.length(); for (int i = 0 ; i < minSize; i++) { for (int x = 0 ,y = minSize - i; y <= minSize; x++,y++){ String subStr = minString.substring(x, y); if (maxString.contains(subStr)){ return subStr; } } } } return null ; } public String[] getMaxString1(String str1,String str2){ if (str1 != null && str2!= null ){ StringBuffer sBuffer = new StringBuffer (); String maxString = (str1.length() >= str2.length()) ? str1 : str2; String minString = (str1.length() < str2.length()) ? str1 : str2; int minSize = minString.length(); for (int i = 0 ; i < minSize; i++) { for (int x = 0 ,y = minSize - i; y <= minSize; x++,y++){ String subStr = minString.substring(x, y); if (maxString.contains(subStr)){ sBuffer.append(subStr+"," ); } } if (sBuffer.length() != 0 ){ break ; } } return sBuffer.toString().replaceAll(",$" ,"" ).split("\\," ); } return null ; } public List<String> getMaxString2 (String str1, String str2) { if (str1 != null && str2!= null ){ ArrayList<String> strs = new ArrayList <String>(); String maxString = (str1.length() >= str2.length()) ? str1 : str2; String minString = (str1.length() < str2.length()) ? str1 : str2; int minSize = minString.length(); for (int i = 0 ; i < minSize; i++) { for (int x = 0 ,y = minSize - i; y <= minSize; x++,y++){ String subStr = minString.substring(x, y); if (maxString.contains(subStr)){ strs.add(subStr); } } if (strs.size() != 0 ){ break ; } } return strs; } return null ; }
==注意==
1 2 3 4 5 6 7 8 9 10 @Test public void test02 () { String str = null ; StringBuffer sb1 = new StringBuffer (); sb1.append(str); System.out.println(sb1.length()); System.out.println(sb1); StringBuffer sb2 = new StringBuffer (str); System.out.println(sb2); }
常用类jdk1.8新日期时间类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 @Test public void DateTest () { Calendar instance = Calendar.getInstance(); int days = instance.get(Calendar.DAY_OF_MONTH); System.out.println(days); instance.set(Calendar.DAY_OF_MONTH,10 ); System.out.println(instance.get(Calendar.DAY_OF_MONTH)); instance.add(Calendar.DAY_OF_MONTH,2 ); System.out.println(instance.get(Calendar.DAY_OF_MONTH)); instance.add(Calendar.DAY_OF_MONTH,-5 ); System.out.println(instance.get(Calendar.DAY_OF_MONTH)); Date time = instance.getTime(); SimpleDateFormat simple = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); System.out.println(simple.format(time)); instance.setTime(new Date ()); System.out.println(instance.get(Calendar.DAY_OF_MONTH)); } @Test public void testLocalDateTime () { LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate); System.out.println(localTime); System.out.println(localDateTime); LocalDateTime of = LocalDateTime.of(2020 , 2 , 8 , 13 , 35 , 6 ); System.out.println(of); System.out.println(localDateTime.getDayOfYear()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getYear()); System.out.println(localDateTime.getMonthValue()); System.out.println(localDateTime.getDayOfMonth()); System.out.println(localDateTime.getHour()); System.out.println(localDateTime.getMinute()); System.out.println(localDateTime.getSecond()); System.out.println(localDateTime.getDayOfWeek()); LocalDateTime localDateTime1 = localDateTime.withDayOfMonth(10 ); LocalDateTime localDateTime2 = localDateTime.withMonth(10 ); LocalDateTime localDateTime3 = localDateTime.withDayOfYear(365 ); System.out.println(localDateTime); System.out.println(localDateTime1); System.out.println(localDateTime2); System.out.println(localDateTime3); LocalDateTime localDateTime4 = localDateTime.plusMonths(3 ); System.out.println(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void testInstant () { Instant instant = Instant.now(); System.out.println(instant); OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8 )); System.out.println(offsetDateTime); long milli = instant.toEpochMilli(); System.out.println(milli); Instant instant1 = Instant.ofEpochMilli(1644302483969L ); System.out.println(instant1); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Test public void testFormat () { DateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME; LocalDateTime localDateTime = LocalDateTime.now(); String str1 = formatter1.format(localDateTime); System.out.println(localDateTime); System.out.println(str1); TemporalAccessor parse = formatter1.parse("2022-02-08T15:46:34.603" ); System.out.println(parse); DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); String str2 = formatter2.format(localDateTime); System.out.println(str2); DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); String str3 = formatter3.format(LocalDate.now()); System.out.println(str3); DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ); String str4 = formatter4.format(LocalDateTime.now()); System.out.println(str4); TemporalAccessor parse1 = formatter4.parse("2022-02-08 16:11:43" ); System.out.println(parse1); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void testZone () { Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); for (String availableZoneId : availableZoneIds) { System.out.println(availableZoneId); } LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Aden" )); System.out.println(localDateTime); ZonedDateTime now = ZonedDateTime.now(); ZonedDateTime now1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai" )); System.out.println(now); System.out.println(now1); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testDuration () { LocalDateTime localDateTime1 = LocalDateTime.now(); LocalDateTime localDateTime2 = LocalDateTime.of(2022 ,2 ,9 ,20 ,0 ,0 ); Duration between = Duration.between(localDateTime1, localDateTime2); System.out.println(between.toDays()); System.out.println(between.toHours()); System.out.println(between.toMinutes()); System.out.println(between.getSeconds()); System.out.println(between.toMillis()); }
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testPeriod () { LocalDate localDate1 = LocalDate.now(); LocalDate localDate2 = LocalDate.of(2022 ,2 ,9 ); Period period = Period.between(localDate1, localDate2); System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 步骤: 1. 拿到要转换的Date对象 2. 将Date对象转换成为Instant对象 方法: Date对象.toInstant() 3. 将瞬时对象转换成为LocalDateTime对象 方法: LocalDateTime.ofInstant(瞬时对象,时区); Date date = new Date ();Instant instant = date.toInstant();LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 步骤: 1 ,拿到要转换的LocalDateTime对象2 ,将LocalDateTime对象转换成为时区对象 方法: LocalDateTime对象.atZone(时区); 3. 将2 中得到的时区对象转换成为瞬时对象 方法; 时区对象.toInstant() 4. 通过Date提供的静态方法将3 中得到的瞬时对象转换为我们需要的Date对象 方法: Date date = Date.from(瞬时对象); LocalDateTime dateTime = LocalDateTime.now();ZonedDateTime zonedDateTime = dateTime.atZone(ZoneId.systemDefault());Instant instant2 = zonedDateTime.toInstant();Date date2 = Date.from(instant2);
比较器 java实现排序的方式有两种
其他常用类的使用 System
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test () { String javaVersion = System.getProperty( "java.version" ); System.out.println("java的version: " + javaVersion); String javaHome = System.getProperty("java.home" ); System.out.println("java的home:" +javaHome); String osName = System.getProperty( "os.name" ); System.out.println("os的名称:" +osName); String osVersion = System.getProperty("os.version" ); System.out.println("os的版本:" +osVersion); String userName = System.getProperty( "user.name" ); System.out.println("user的name:" + userName); String userHome = System.getProperty ( "user. home" ); System.out.println( "user的home:" + userHome); String userDir = System.getProperty ("user.dir" ); System.out.println( "user的dir:" + userDir); }
BigInteger和BigDecimal BigInteger
BigDecimal 1. 构造器 1 2 3 4 5 6 7 8 BigDecimal(int ); BigDecimal(double ); BigDecimal(long ); BigDecimal(String);
1 2 3 4 5 6 7 8 9 10 BigDecimal bg1 = new BigDecimal (0.1 );System.out.println("a values is:" +bg1); System.out.println("=====================" ); BigDecimal bg2 = new BigDecimal ("0.1" );System.out.println("b values is:" +bg2); System.out.println("=====================" ); System.out.println(bg1 == bg2); System.out.println(bg1.equals(bg2)); System.out.println("=====================" ); System.out.println(BigDecimal.valueOf(0.1 ));
参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625
。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值) 。
String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法 。
如果要将一个double转称BigDecimal时,使用BigDecimal类所提供的静态方法valueOf(double)
。
2. 常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 add(BigDecimal); subtract(BigDecimal); multiply(BigDecimal); divide(BigDecimal); toString(); doubleValue(); floatValue(); longValue(); intValue(); bg1.compareTo(bg2);
举例:
1 2 3 4 5 6 7 8 9 10 @Test public void test02 () { BigDecimal bg1 = new BigDecimal (0.1 ); BigDecimal bg2 = new BigDecimal ("0.1" ); int i = bg1.compareTo(bg2); System.out.println(i); }
3. 格式化 由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。
以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test03 () { NumberFormat currency = NumberFormat.getCurrencyInstance(); NumberFormat percent = NumberFormat.getPercentInstance(); percent.setMaximumFractionDigits(3 ); BigDecimal loanAmount = new BigDecimal ("15000.48" ); BigDecimal interestRate = new BigDecimal ("0.008" ); BigDecimal interest = loanAmount.multiply(interestRate); System.out.println("贷款金额:\t" + currency.format(loanAmount)); System.out.println("利率:\t" + percent.format(interestRate)); System.out.println("利息:\t" + currency.format(interest)); }
4. 注意事项 除法会出现的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test04 () { BigDecimal bg1 = new BigDecimal ("3.3213" ); BigDecimal bg2 = new BigDecimal ("2.43214" ); System.out.println(bg1.divide(bg2,2 ,BigDecimal.ROUND_HALF_UP)); }
**注意:**BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
1 2 3 4 5 6 7 8 9 @Test public void test05 () { BigDecimal bg1 = new BigDecimal ("1" ); BigDecimal bg2 = new BigDecimal ("9" ); bg1.add(bg2); System.out.println(bg1); bg1 = bg1.add(bg2); System.out.println(bg1); }
枚举类和注解 枚举 枚举类的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class Season { private final String seasonName; private final String seasonDesc; private Season (String seasonName,String seasonDesc) { this .seasonName = seasonName; this .seasonDesc = seasonDesc; } public static final Season SPRING = new Season ("春天" ,"春暖花开" ); public static final Season SUMMER = new Season ("夏天" ,"夏日炎炎" ); public static final Season AUTUMN = new Season ("秋天" ,"秋高气爽" ); public static final Season WINTER = new Season ("冬天" ,"冰天雪地" ); public String getSeasonName () { return seasonName; } public String getSeasonDesc () { return seasonDesc; } @Override public String toString () { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}' ; } } enum Season1 { SPRING("春天" ,"春暖花开" ), SUMMER("夏天" ,"夏日炎炎" ), AUTUMN("秋天" ,"秋高气爽" ), WINTER("冬天" ,"冰天雪地" ); private final String seasonName; private final String seasonDesc; Season1(String seasonName,String seasonDesc){ this .seasonName = seasonName; this .seasonDesc = seasonDesc; } public String getSeasonName () { return seasonName; } public String getSeasonDesc () { return seasonDesc; } } @Test public void test () { Season1 autumn = Season1.AUTUMN; System.out.println(autumn); }
Enum类的常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test () { Season1 autumn = Season1.AUTUMN; System.out.println(autumn.toString()); Season1[] values = Season1.values(); for (Season1 value : values) { System.out.println(value); } Season1 spring = Season1.valueOf("SPRING" ); System.out.println(spring); }
使用enum关键字定义的枚举实现接口的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 interface Info { void show () ; } enum Season1 implements Info { SPRING("春天" ,"春暖花开" ){ @Override public void show () { System.out.println("春天在哪里" ); } }, SUMMER("夏天" ,"夏日炎炎" ) { @Override public void show () { System.out.println("夏天在哪里" ); } }, AUTUMN("秋天" ,"秋高气爽" ) { @Override public void show () { System.out.println("秋天在哪里" ); } }, WINTER("冬天" ,"冰天雪地" ) { @Override public void show () { System.out.println("冬天在哪里" ); } }; private final String seasonName; private final String seasonDesc; Season1(String seasonName,String seasonDesc){ this .seasonName = seasonName; this .seasonDesc = seasonDesc; } public String getSeasonName () { return seasonName; } public String getSeasonDesc () { return seasonDesc; } } @Test public void test () { Season1 autumn = Season1.AUTUMN; System.out.println(autumn.toString()); autumn.show(); }
注解 自定义注解
注解声明为@interface
内部定义成员,通常使用value表示
可以指定成员的默认值,使用default定义
如果自定义注解没有成员,表明这是一个标识作用
1 2 3 4 5 6 7 8 public @interface MyAnnotation { String value () default "hello" ; } @MyAnnotation(value = "hello") public class AnnotationTest { }
注意:自定义注解必须配上注解的信息处理流程(使用反射)才有意义
元注解:JDK中用于修饰其他(Annotation)注解的定义,一般自定义注解都需要指明@Retention和@Target
@Retention
只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期,@Retention包含一个RetentionPolicy类型的成员变量,使用@Retention时必须指定该value的成员变量指定值:
RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释
RetentionPolicy.CLASS:在class文件中有效(即class文件保留),当运行java程序时,JVM不会保留注释。==默认值==
RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行java程序时JVM会保留注释。程序可以。程序可以通过反射获取该注释。
@Target
用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰那些程序元素。
@Documented
用于指定被该Annotation修饰的Annotation类将被javadoc工具提取成文档。默认情况下,javadoc是不包含注解的。
定义为Documented的注解必须设置Retention的值为RUNTIME
@Inherited
被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则子类将自动具有该注解。
集合 数组在存储多个数据方面的特点:
一旦初始化以后长度就已经确定
数组一旦定义好,其元素的类型就已经确定了
数组在存储多个数据方面的弊端:
一旦初始化,长度就不可修改
数组中提供的方法非常有限,对于添加,插入,删除操作非常不便,同时效率不高
获取数组中实际元素的个数,数组中没有现成的属性或者方法可用
数组存储的特点:有序,可重复。对于无序,不可重复的需求,不能满足
集合分为两种体系
Collection 常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 add(Object obj); addAll(Collection coll); int size () ;void clear () ;boolean isEmpty () ;boolean contains (Object obj) ; boolean containsAll (Collection coll) ; boolean remove (Object obj) ;boolean removeAll (Collection coll) ;boolean retainAll (Coolection coll) ; boolean equals (Object obj) ;Object toArray () ; hashCode(); iterator();
iterator()的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void test () { Collection coll = new ArrayList (); coll.add("abc" ); coll.add("12345" ); Iterator iterator = coll.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println(iterator.next()); }
ArrayList、LinkedList、Vector三者的异同?
同:都实现了List接口,存储数据的特点相同:都是存储有序,可重复数据。
不同:见上
List ==ArrayList源码分析==
jdk1.7
new ArrayList()对象底层创建的是一个长度为10的Object[]数组
当新添加的元素超过10时,默认情况下扩容为原来的1.5倍,将原有数据添加到新的数组中。
jdk1.8
new ArrayList()对象底层Object[] elementDate初始化为{},并没有创建长度为10的数组
第一次调用add()方法时,底层创建了长度为10的数组,并把值添加到elementDate中
常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void add (int index,Object ele) ;boolean addAll (int index,Collection coll) ;Object getIndex (int index) ; int indexOf (Object obj) ;int lastIndextOf (Object obj) ;Object remove (Object obj) ; Object set (int index,Object ele) ; List subList (int fromIndex,int toIndex) ;
向Collection接口实现类的对象添加数据obj时,要求obj所在的类必须重写equals()方法
Set 特点: 存储无序的,不可重复数据。
以hashSet为例说明:
无序性:不等于随机性。存储的数据在底层数组中并未安装数组索引的数据添加,而是根据数据的哈希值决定的。
不可重复性:保证添加的元素按照equals()判断时 ,不能返回true即可,即相同元素只有一个
我们向hashSet中添加元素a,首先调用a所在类的hashCode()方法,计算a的哈希值,此哈希值接着通过某种算法计算出在hashSet底层数组中存放的位置(即索引位置),判断数组此位置是否已经有元素:
如果此位置没有其他元素,a添加成功
如果此位置上有其他元素b(或以链表形式存在的多个元素),首先比较元素a和元素b的哈希值是否相同
如果不相同,则元素a添加成功
如果相同,进而调用元素所在类的equals()方法
如果返回ture,元素a添加失败
如果返回false,元素a添加成功
HashSet
作为Set的接口的主要实现类;线程不安全的;可存储null值,数组+链表
要求:
向Set中添加的数据,其所在类一定要重写hashCode()和equals()
重写的hashCode()和equals()尽可能保持一致性。
TreeSet
可以按照添加对象的指定属性,进行排序。不可以添加不同类的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void test04 () { HashSet set = new HashSet (); Person p1 = new Person (1001 , "AA" ); Person p2 = new Person (1002 , "BB" ); set.add(p1); set.add(p2); p1.setName("CC" ); set.remove(p1); System.out.println(set); set.add(new Person (1001 ,"CC" )); System.out.println(set); set.add(new Person (1001 ,"AA" )); System.out.println(set); }
Map 双列数据,存储Kye-Value对数据
HashMap
作为Map的主要实现类,线程不安全,效率高;可以存储null的key和value
TreeMap
保证按照添加的key-value对进行排序,实现排序遍历。此时考虑的是key的排序。底层使用的是红黑树
HashTable
做为Map的古老实现类,线程安全的,效率低;不可以存储null的key和value
HashMap的底层
jdk7之前:数组+链表
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
map.put(key1,value1)
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值在经过某种算法以后,得到Entry数组中的存放位置。如果此位置上的数据为空,此时的key1-value1添加成功。
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在一个或多个数据的hash值:
如果key1的哈希值与已经存在的数据哈希值都不相同,此时key1-value1添加成功。
如果key1的哈希值和已经存在的某一个哈希值相同了,则继续比较key1所在类的equals()方法。
如果equals()方法返回false,则key1-value1添加成功
如果equals()方法返回true,使用value1替换相同key的value值
在不断添加过程中,会涉及到扩容问题,当超出临界值(要存放的位置非空时),默认扩容方式为原来的2倍,并把原来的数据复制过来
jdk8:数组+链表+红黑树
new HashMap():底层没有创建一个长度为16的数组
jdk8底层的数组是Node[] 不是 Entry[]
首次调用put()方法时才调用长度为16的数组
当数组的某一个索引位置上的元素以链表形式存在的个数 > 8 且当前数组的长度 > 64
此时索引的位置上的所有数据改为红黑树存储。
常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Object put (Object key,Object value) ; void putAll (Map map) ;Object remove (Object key) ; void clear () ;Object get (Object key) ; boolean containsKey (Object key) ;boolean contatinsValue (Object value) ;int size () ;boolean isEmpty () ;boolean equals (Object obj) ;Set keySet () ; Collection values () ; Set entrySet () ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Test public void testHashMap () { Map map = new HashMap <>(); map.put("AA" ,12 ); map.put("BB" ,72 ); map.put("CC" ,42 ); map.put("DD" ,62 ); Set set = map.keySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } Collection values = map.values(); for (Object value : values) { System.out.println(value); } Set entrySet = map.entrySet(); for (Object o : entrySet) { Map.Entry entry = (Map.Entry) o; System.out.println(entry.getKey()+"-" +entry.getValue()); } }
1 2 3 4 5 6 7 8 @Test public void TestProperties () throws IOException { Properties properties = new Properties (); FileInputStream fis = new FileInputStream ("jdbc.properties" ); properties.load(fis); String name = properties.getProperty("name" ); System.out.println(name); }
Collections工具类 Collections是一个操作Set、List和Map的工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 reverse(List); shuffle(List); sort(List); sort(List,Comparator); swap(List,int ,int ); Object max (Collection) ; Object max (Collection,Comparator) ; Object min (Collection) ; Object min (Collection,Comparator) ; int frequency (Collection,Object) ;void copy (List dest,List src) ;boolean replaceAll (List list,Object oldVal,Object newVal) ;
Collection中提供了多个synchronizedXxx()方法,该方法可将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。
1 2 3 4 5 6 synchronizedCollection(Collection<T> coll); synchronizedList(List<T> list); synchronizedMap(Map<K,V> map); synchronizedSet(Set<T> set); synchronizedSortedMap(Map<K,V> map); synchronizedSortedSet(Set<T> set);
泛型 所谓的泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法防止及参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量,创建对象时)确定(即传入实际的类型参数,也称为类型变量)。
自定义泛型结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Order <T>{ String orderName; String orderId; T orderDesc; public Order () {} public Order (String orderName, String orderId, T orderDesc) { this .orderName = orderName; this .orderId = orderId; this .orderDesc = orderDesc; } public T getOrderDesc () { return orderDesc; } public void setOrderDesc (T orderDesc) { this .orderDesc = orderDesc; } } public class SubOrder extends Order <String> {}public class SubOrder1 <T> extends Order <T>{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test1 () { Order order = new Order (); order.setOrderDesc("ABC" ); order.setOrderDesc(12 ); Order<String> stringOrder = new Order <>(); stringOrder.setOrderDesc("ABC" ); SubOrder subOrder = new SubOrder (); subOrder.setOrderDesc("ABC" ); SubOrder1<String> stringSubOrder1 = new SubOrder1 <>(); stringSubOrder1.setOrderDesc("ABC" ); }
泛型注意点:
泛型方法 1 2 3 4 5 6 7 8 public <E> List<E> copyFromArray (E[] array) { ArrayList<E> es = new ArrayList <>(); for (E e : array) { es.add(e); } return es; }
泛型方法可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化时确定的。
泛型在继承方面的体现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void test03 () { Object obj = null ; String str = null ; obj = str; List<Integer> integers = null ; List<String> strings = null ; List<String> list1 = new ArrayList <>(); ArrayList<String> list2 = new ArrayList <>(); list1 = list2; }
通配符的使用==(?)==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void test04 () { List<Object> list1 = null ; List<String> list2 = null ; List<?> list = null ; list = list1; list = list2; } public void print (List<?> list) { Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } @Test public void test05 () { List<String> list2 = new ArrayList <>(); list2.add("AA" ); list = list2; System.out.println(list.get(0 )); }
有限制条件的通配符的使用
extends Number> (无穷小,Number)
只允许泛型为Number及Number的子类引用调用
super Number> (Number,无穷大)
只允许泛型为Number及Number父类的引用调用
extends Comparaable>
只允许泛型为实现Comparable接口的实现类的引用调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public void test06 () { List<? extends Person > list1 = null ; List<? super Person> list2 = null ; List<Student> list3 = new ArrayList <>(); List<Person> list4 = new ArrayList <>(); List<Object> list5 = new ArrayList <>(); list1 = list3; list1 = list4; list2 = list4; list2 = list5; list1 = list4; Person person = list1.get(0 ); list2 = list4; Object object = list2.get(0 ); list2.add(new Student ()); list2.add(new Person ()); }
帮助理解:
读数据:
extends Person>只允许泛型为Person及Person的子类引用调用(<=Person),代表可以使用子类Student对象赋值,如果list1=list4,那么list1.get(0)取出肯定是一个Person对象,因此用其子类Student接收不行,编译报错
super Person>代表只允许泛型为Person及Person的父类引用调用(>=Person),代表可以使用父类Object对象赋值,,如果list2=list5,那么list2.get(0)取出肯定是一个Object对象,因此用其子类Person接收不行,编译报错
IO流 File类的使用 java.io.File:文件和文件目录的抽象表示形式,与平台无关
File能新建、删除、重命名文件和目录,但File不能范文文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
构造器:
1 2 3 4 5 6 public File (String pathname) ;public File (String parent,String child) ;public File (File parent,String child) ;
常用方法
获取功能的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 String getAbsolutePath () ; String getPath () ; String getName () ; String getParent () ; long length () ;long lastModified () ;String[] list(); File[] listFiles(); boolean renameTo (File dest) ;
判断功能的方法
1 2 3 4 5 6 7 8 9 10 11 12 boolean isDirectory () ;boolean isFile () ;boolean exists () ;boolean canRead () ;boolean canWrite () ;boolean isHidden () ;
创建和删除
1 2 3 4 5 6 7 8 boolean createNewFile () ;boolean mkdir () ;boolean mkdirs () ;boolean delete () ;
过滤器 在File类的list方法中可以接受一个FilenameFiter参数,通过参数可以仅列出符合条件的文件。
FilenameFilter接口仅包含一个accept(File dir,String name)方法,该方法将依次对指定的File的子目录或对文件进行迭代,如果该方法返回回true,则list方法会列出该子目录或文件。
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test3 () { File file = new File ("E:\\javaProject\\JavaBasics" ); file.list(new FilenameFilter () { @Override public boolean accept (File dir, String name) { return name.endsWith(".java" ); } }); System.out.println(file.getName()); }
IO流原理及流的分类
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
输入input:读取外部数据(磁盘,光盘等存储设备的数据)到程序(内存中)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
java程序中,对于数据的输入/输出操作及”流(stream)”的方式进行。
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
流的分类
按照操作的数据单位不同分为:字节流(8bit),字符流(16bit)
按照数据流的流向不同分为:输入流和输出流
按照流的角色不同分为:节点流,处理流
抽象基类
字节流
字符流
输入流
InputStream
Reader
输出流
OutputStream
Writer
FileReader 和 FileWriter FileReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Test public void testFileReader () { File file = new File ("hi.txt" ); FileReader fr = null ; try { fr = new FileReader (file); char [] cbuf = new char [5 ]; int len; while ((len = fr.read(cbuf)) != -1 ){ System.out.print(new String (cbuf,0 ,len)); } } catch (IOException e) { e.printStackTrace(); }finally { if (fr != null ){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
FileWriter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void testFileWriter () { File file = new File ("hello.txt" ); FileWriter fw = null ; try { fw = new FileWriter (file,false ); fw.write("is not null" ); fw.write("hao are you?" ); } catch (IOException e) { e.printStackTrace(); }finally { if (fw != null ){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
综合案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Test public void testReadWriteTest () { FileReader fr = null ; FileWriter fw = null ; try { fr = new FileReader (new File ("hi.txt" )); fw = new FileWriter (new File ("hello.txt" )); char [] str = new char [5 ]; int len; while ((len = fr.read(str)) != -1 ){ fw.write(str,0 ,len); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (fr != null ) fr.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fw != null ) fw.close(); } catch (IOException e) { e.printStackTrace(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void testFileInputStream () { FileInputStream fis = null ; try { File file = new File ("hello.txt" ); fis = new FileInputStream (file); byte [] buffer = new byte [1024 ]; int len; while ((len = fis.read(buffer)) != -1 ){ System.out.println(new String (buffer,0 ,len)); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fis != null ) fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
综合案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void testFileInputStream () { FileInputStream fis = null ; FileOutputStream fos = null ; try { File input = new File ("test.pdf" ); File output = new File ("test1.pdf" ); fis = new FileInputStream (input); fos = new FileOutputStream (output); byte [] buffer = new byte [1024 ]; int len; while ((len = fis.read(buffer)) != -1 ){ fos.write(buffer,0 ,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fis != null ) fis.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fos != null ) fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
处理流之一:缓冲流 作用:提高流的读取,写入的速度。能提高速度的原因是缓冲流内部提供了一个缓冲区
BufferedInputStream、BufferedOutputStream、BuffereReader、Buffered Writer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Test public void testBufferedStream () { BufferedInputStream bis = null ; BufferedOutputStream bos = null ; try { File input = new File ("test.pdf" ); File output = new File ("test1.pdf" ); FileInputStream fis = new FileInputStream (input); FileOutputStream fos = new FileOutputStream (output); bis = new BufferedInputStream (fis); bos = new BufferedOutputStream (fos); byte [] buffer = new byte [1024 ]; int len; while ((len = bis.read(buffer)) != -1 ){ bos.write(buffer,0 ,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (bos != null ) bos.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bis != null ) bis.close(); } catch (IOException e) { e.printStackTrace(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Test public void testBufferedReaderWriter () { BufferedReader br = null ; BufferedWriter bw = null ; try { FileReader fr = new FileReader (new File ("hi.txt" )); FileWriter fw = new FileWriter (new File ("hello.txt" )); br = new BufferedReader (fr); bw = new BufferedWriter (fw); String str; while ((str = br.readLine()) != null ){ bw.write(str); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (bw != null ) bw.close(); } catch (IOException e) { e.printStackTrace(); } try { if (br != null ) br.close(); } catch (IOException e) { e.printStackTrace(); } } }
处理流之二:转换流 转换流提供了在字节流和字符流之间的转换
InputStreamReade:将InputStream转换为Reader
OoutputStreamWriter:将Writer转换为OutputStream
将输入的字节流转换为输入的字符流
将输出的字符流转换为输出的字节流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void testInputStreamReader () { InputStreamReader isr = null ; try { FileInputStream fis = new FileInputStream ("hello.txt" ); isr = new InputStreamReader (fis, "utf-8" ); char [] cbuf = new char [1024 ]; int len; while ((len = isr.read(cbuf)) != -1 ){ System.out.println(new String (cbuf,0 ,len)); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (isr != null ) isr.close(); } catch (IOException e) { e.printStackTrace(); } } }
综合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Test public void test () { InputStreamReader isr = null ; OutputStreamWriter osw = null ; try { FileInputStream fis = new FileInputStream ("hello.txt" ); FileOutputStream fos = new FileOutputStream ("hello1.txt" ); isr = new InputStreamReader (fis, "utf-8" ); osw = new OutputStreamWriter (fos,"gbk" ); char [] cbuf = new char [1024 ]; int len; while ((len = isr.read(cbuf)) != -1 ){ osw.write(cbuf,0 ,len); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (osw != null ) osw.close(); } catch (IOException e) { e.printStackTrace(); } try { if (isr != null ) isr.close(); } catch (IOException e) { e.printStackTrace(); } } }
处理流之三:对象流 ObjectInputStream和ObjectOutputStream
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把java中的对象写入到数据源中,也能把对象从数据源中还原回来
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
ObjectInputStream和ObjectOutputStream不能序列化static和transient修饰的成员变量
类需要进行序列化需要满足如下条件
序列化和反序列化类必须是实现了==Serializable==或Externalizable 接口。
需要提供一个全局常量serialVersionUID
除了当前类需要实现==Serializable==外,还必须保证其内部的所有属性也必须是可序列化的。(默认情况下基本数据类型都是可序列化的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void test () { ObjectOutputStream oos = null ; try { oos = new ObjectOutputStream (new FileOutputStream ("object.dat" )); oos.writeObject(new Person (1 ,"李华" )); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (oos != null ) oos.close(); } catch (IOException e) { e.printStackTrace(); } finally { } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void test1 () { ObjectInputStream ois = null ; try { ois = new ObjectInputStream (new FileInputStream ("object.dat" )); Person str = (Person) ois.readObject(); System.out.println(str); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (ois != null ) ois.close(); } catch (IOException e) { e.printStackTrace(); } finally { } } }
网络编程
计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源.
网络编程的目的
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯
网络编程中有两个主要问题
如何准确地定位网络上一台或多台主机、定位主机上的特定的应用
找到主机后如何可靠高效地进行数据传输
网络通信的要素
IP和端口号
IP地址:InetAddress
唯一标识Internet上的计算机(通信实体)
本地回环地址:127.0.0.1、主机名(localhost)
IP地址的分类1:IPV4和IPV6
IPV4:4个字节组成,4个0-255。大概42亿,30亿在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
IPV6:128位(16个字节),写成8个无符号整数,每个整数用4个十六进制位表示,数之间用冒号(:)分开。
IP地址分类2:公网地址(万维网使用)和私有地址(局域网使用)。192.168开头的就是私有地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。
端口号:端口号标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号
被规定为一个16位的整数0~65535。
端口分类:
公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
注册端口:1024~49151。分配给用户进程或应用程序。(如: Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
动态/私有端口:49152~65535。
端口号与IP地址的组合得出一个网络套接字:Socket。
网络通信协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void test01 () { try { InetAddress inet1 = InetAddress.getByName("192.168.10.14" ); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("www.atguigu.com" ); System.out.println(inet2); String hostAddress = inet2.getHostAddress(); String hostName = inet2.getHostName(); System.out.println(hostAddress); System.out.println(hostName); InetAddress localhost1 = InetAddress.getByName("localhost" ); InetAddress localHost2 = InetAddress.getLocalHost(); System.out.println(localhost1); System.out.println(localHost2); } catch (UnknownHostException e) { e.printStackTrace(); } }
TCP的网络编程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 public class TcpTest1 { @Test public void client () { Socket socket = null ; OutputStream out = null ; try { InetAddress inet = InetAddress.getByName("localhost" ); socket = new Socket (inet,8889 ); out = socket.getOutputStream(); out.write("你好,我是客户端" .getBytes()); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (socket != null ) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null ) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server () { ServerSocket serverSocket = null ; Socket socket = null ; InputStream input = null ; ByteArrayOutputStream baos = null ; try { serverSocket = new ServerSocket (8889 ); socket = serverSocket.accept(); input = socket.getInputStream(); baos = new ByteArrayOutputStream (); byte [] buffer = new byte [1024 ]; int len; while ((len = input.read(buffer)) != -1 ){ baos.write(buffer,0 ,len ); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); } finally { try { if (baos != null ) baos.close(); if (input != null ) input.close(); if (socket != null ) socket.close(); if (serverSocket != null ) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public class TcpTest2 { @Test public void client () { Socket socket = null ; OutputStream out = null ; try { InetAddress inet = InetAddress.getByName("localhost" ); socket = new Socket (inet,8889 ); out = socket.getOutputStream(); File file = new File ("hello.txt" ); InputStream is = new FileInputStream (file); byte [] buffer = new byte [1024 ]; int len; while (( len = is.read(buffer)) != -1 ){ out.write(buffer,0 ,len); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (socket != null ) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null ) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void server () { ServerSocket serverSocket = null ; Socket socket = null ; FileOutputStream out = null ; InputStream input = null ; try { serverSocket = new ServerSocket (8889 ); socket = serverSocket.accept(); input = socket.getInputStream(); out = new FileOutputStream ("hello2.txt" ); byte [] buffer = new byte [1024 ]; int len; while ((len = input.read(buffer)) != -1 ){ out.write(buffer,0 ,len ); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null ) out.close(); if (input != null ) input.close(); if (socket != null ) socket.close(); if (serverSocket != null ) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
反射 Java反射机制的概述 Reflection
(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助ReflectionAPI取得任何类的内部结构信息,并能直接操作任意对象的内部属性及方法。
加载玩类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这面镜子看到类的结构,所以我们形象称之为:反射
动态语言 VS 静态语言
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言 的特性。
Java的动态性让编程的时候更加灵活!
JAVA反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
反射相关的API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
……
疑问:反射机制与面向对象中的封装性是不是矛盾?如何看待这两个技术。
不矛盾。封装性体现的是建议你怎么调用,反射体现的是可不可以调。
理解Class类并获取Class类实例 关于java.lang.Class类的理解
类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class)结尾。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就被称为类的加载过程。加载到内存中的类,我们就称为运行时类 ,此运行时类,就作为一个Class
类。
换句话说,Class
的实例就对应着一个运行时类。
加载到内存中的运行时类,会换成一段时间。在此期间之内,我们可以通过不同的方式来获取此运行时类的对象。
关于Class
类的理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void test03 () { Class<Object> objectClass = Object.class; Class<Comparable> comparableClass = Comparable.class; Class<String[]> aClass = String[].class; Class<int [][]> aClass1 = int [][].class; Class<ElementType> elementTypeClass = ElementType.class; Class<Override> overrideClass = Override.class; Class<Integer> integerClass = int .class; Class<Void> voidClass = void .class; Class<Class> classClass = Class.class; int [] a = new int [10 ]; int [] b = new int [100 ]; System.out.println(a.getClass() == b.getClass()); }
获取Class
实例的四种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void test02 () throws ClassNotFoundException { Class<Person> clazz1 = Person.class; Person p1 = new Person (); Class clazz2 = p1.getClass(); Class clazz3 = Class.forName("reflectionTest.Person" ); ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("reflectionTest.Person" ); System.out.println(clazz1); System.out.println(clazz2); System.out.println(clazz3); System.out.println(clazz4); }
类的加载与ClassLoder的理解 当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对类进行初始化。
了解ClassLoader(类加载器)
类的加载器作用是把类(class)装载到内存的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test04 () throws Exception { Properties pros = new Properties (); ClassLoader classLoader = ReflectionTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc.properties" ); pros.load(is); String username = pros.getProperty("username" ); String password = pros.getProperty("password" ); System.out.println("username=" +username+",password=" +password); }
创建运行时类的对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void test05 () { try { Class<Person> clazz = Person.class; Person person = clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
反射性的体现,运行时创建类的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public Object getInstance (String classPath) throws Exception { Class<?> clazz = Class.forName(classPath); return clazz.newInstance(); } @Test public void test06 () { int num = new Random ().nextInt(3 ); String classPath = "" ; switch (num){ case 0 : classPath = "java.util.Date" ; break ; case 1 : classPath = "java.lang.Object" ; break ; case 2 : classPath = "reflectionTest.Person" ; break ; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } }
获取运行时类的完整结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 public interface MyInterface { void info () ; } public class Creature <T> implements Serializable { private char gender; public double weight; private void breath () { System.out.println("生物呼吸" ); } public void eat () { System.out.println("生物吃东西" ); } } @Target({ElementType.TYPE,ElementType.METHOD,ElementType.PACKAGE}) @Retention(RetentionPolicy.SOURCE) public @interface MyAnnotation { String value () ; } @MyAnnotation(value = "hi") public class Person extends Creature <String> implements Comparable <String>,MyInterface{ private String name; int age; public int id; public Person () {} private Person (String name) { this .name = name; } public Person (int age, String name) { this .age = age; this .name = name; } Person(String name, int age, int id) { this .name = name; this .age = age; this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public int getId () { return id; } public void setId (int id) { this .id = id; } private String show (String nation) { System.out.println("我的国籍是" +nation); return nation; } public String display (String interests) { System.out.println("我的兴趣是" +interests); return interests; } @Override public int compareTo (String o) { return 0 ; } @Override public void info () { System.out.println("我是一个人" ); } }
获取运行时属性的结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Test public void test07 () { Class<Person> clazz = Person.class; Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); } Field[] declaredFields = clazz.getDeclaredFields(); for (Field f : declaredFields) { System.out.println(f); int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier)+"\t" ); Class type = f.getType(); System.out.print(type+"\t" ); String name = f.getName(); System.out.print(name+"\t" ); System.out.println(); } }
获取运行时类的方法结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Test public void test08 () { Class<Person> clazz = Person.class; Method[] methods = clazz.getMethods(); Method[] dms = clazz.getDeclaredMethods(); for (Method m : dms) { Annotation[] annotations = m.getAnnotations(); int modifiers = m.getModifiers(); String returnType = m.getReturnType().getName(); String name = m.getName(); Class<?>[] parameterTypes = m.getParameterTypes(); Class<?>[] exceptionTypes = m.getExceptionTypes(); } }
获取构造器结构
1 2 3 4 5 6 7 8 @Test public void test09 () { Class<Person> clazz = Person.class; Constructor<?>[] constructors = clazz.getConstructors(); Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); }
获取运行时类的父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void test10 () { Class<Person> clazz = Person.class; Class constructors = clazz.getSuperclass(); Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { String typeName1 = actualTypeArgument.getTypeName(); String typeName2 = ((Class) actualTypeArgument).getName(); } }
获取运行时类实现的接口、所在的包
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test11 () { Class<Person> clazz = Person.class; Class<?>[] interfaces = clazz.getInterfaces(); Class<?>[] interfaces1 = clazz.getSuperclass().getInterfaces(); Package pack = clazz.getPackage(); Annotation[] annotations = clazz.getAnnotations(); }
调用运行时类的指定结构 操作运行时类的指定方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void testFiled () { try { Class<Person> clazz = Person.class; Person person = clazz.newInstance(); Field name = clazz.getDeclaredField("name" ); name.setAccessible(true ); name.set(person,"小明" ); Object o = name.get(person); System.out.println(o); } catch (Exception e) { e.printStackTrace(); } }
操作运行时类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Test public void testMethod () { try { Class<Person> clazz = Person.class; Person person = clazz.newInstance(); Method show = clazz.getDeclaredMethod("show" , String.class); show.setAccessible(true ); Object chn = show.invoke(person, "CHN" ); System.out.println(chn); Method showDesc = clazz.getDeclaredMethod("showDesc" ); showDesc.setAccessible(true ); showDesc.invoke(Person.class); } catch (Exception e) { e.printStackTrace(); } }
操作指定构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testConstructor () { try { Class<Person> clazz = Person.class; Constructor<Person> pc = clazz.getDeclaredConstructor(String.class); pc.setAccessible(true ); Person p = pc.newInstance("Tom" ); System.out.println(p); } catch (Exception e) { e.printStackTrace(); } }
反射的应用:动态代理 代理设计模式的原理
使用一个代理将对象包起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象觉得是否以及何时将方法调用转到原始对象身上。
动态代理模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 interface Human { String getBelief () ; void eat (String foot) ; } class SuperMan implements Human { @Override public String getBelief () { return "I believe I can fly!" ; } @Override public void eat (String foot) { System.out.println("我喜欢吃" +foot); } } class ProxyFactory { public static Object getProxyInstance (Object obj) { MyInvocationHandler myInvocationHandler = new MyInvocationHandler (); myInvocationHandler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),myInvocationHandler); } } class MyInvocationHandler implements InvocationHandler { private Object obj; public void bind (Object obj) { this .obj = obj; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Object returnValue = method.invoke(obj,args); return returnValue; } } public class ProxyTest { public static void main (String[] args) { SuperMan superMan = new SuperMan (); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("馒头" ); } }
Java8新特性 Lambda表达式 Lambda是一个==匿名函数==,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传输)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java语言表达能力得到提升。
Lambda表达式的本质:作为函数式接口的实例
如果需要使用Lambda表达式,则接口必须是函数式接口(只有一个抽象方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class LambdaTest { @Test public void testLambda () { Runnable r1 = new Runnable () { @Override public void run () { System.out.println("AAA" ); } }; r1.run(); Runnable r2 = () -> System.out.println("AAA" ); r2.run(); Consumer<String> c1 = new Consumer <String>() { @Override public void accept (String s) { System.out.println(s); } }; c1.accept("谎言和誓言的区别是什么呢?" ); Consumer<String> c2 = (String s) -> { System.out.println(s); }; c2.accept("一个是听的人当真了,一个是说的人当真了" ); Consumer<String> c3 = (s) -> { System.out.println(s); }; c3.accept("一个是听的人当真了,一个是说的人当真了" ); Consumer<String> c4 = s -> { System.out.println(s); }; c4.accept("一个是听的人当真了,一个是说的人当真了" ); Comparator<Integer> cp1 = new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { System.out.println(o1.equals(o2)); return o1.compareTo(o2); } }; Comparator<Integer> cp2 = (o1, o2) -> { System.out.println(o1.equals(o2)); return o1.compareTo(o2); }; Comparator<Integer> cp3 = (o1, o2) -> o1.compareTo(o2); } }
总结
-> 左边:Lambda形参列表的参数类型可以省略(类型推断);如果只有一个参数括号可以省略,如果有两个以上的参数不可以省略
-> 右边:Lambda使用一对大括号进行包裹;如果执行体只有一条语句(return语句),可以省略{}
和return
关键字。
函数式接口
什么是函数式接口?
只包含一个抽象方法的接口,称为函数式接口
你可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受减异常(非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)
我们可以在一个接口上使用@Functionallnterface
注解,这样做可以检查它是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function
包下定义了丰富的函数式接口
Java内置的四大函数式接口
函数式接口
参数类型
返回类型
用途
Consumer
T
void
对类型为T的对象应用操作,包含方法void accept(T t)
Supplier
无
T
返回类型为T的对象,包含方法T get()
Function<T,R>
T
R
对类型T的对象应用操作,并返回接口。结果为R类型的对象。包含方法:R apply(T t)
Predicate
T
boolean
确认类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t)
方法引用和构造引用 方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:==实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保存一致==(适用于情况一和情况二)
格式:使用操作符”::
“类或对象与方法名区分开来。
具体情况使用
**情况一:**对象 :: 非静态方法
1 2 3 4 5 6 7 8 @Test public void testLambda2 () { Person person = new Person (23 ,"Tom" ); Supplier supplier = () -> person.getName(); System.out.println(supplier.get()); Supplier supplier1 = person::getName; System.out.println(supplier1.get()); }
**情况二:**类 :: 静态方法
1 2 3 4 5 6 7 8 9 10 11 @Test public void testLambda3 () { Comparator<Integer> c1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(c1.compare(12 ,24 )); Comparator<Integer> c2 = Integer::compare; System.out.println(c2.compare(12 ,24 )); }
**情况三:**类 :: 非静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testLambda4 () { Function<Person,String> f = new Function <Person, String>() { @Override public String apply (Person person) { return person.getName(); } }; System.out.println(f.apply(new Person (12 ,"TOM" ))); Function<Person,String> f1 = person -> person.getName(); System.out.println(f1.apply(new Person (12 ,"TOM" ))); Function<Person,String> f2 = Person::getName; System.out.println(f2.apply(new Person (12 ,"TOM" ))); }
构造器引用
1 2 3 4 5 6 7 8 9 @Test public void testLambda5 () { Supplier<Person> person = () -> new Person (); Supplier<Person> person1 = Person::new ; }
Stream API Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个则是Stream API。
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。**Stream API 对集合数据进行操作,就类似于使用SQL执行的数据库查询。**也可以使用Stream API来并行执行操作。简而言之。Stream提供了一种高效且易于使用的处理数据方式。
Stream和Collection的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的。
操作Stream的三个步骤
创建Stream 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test1 () { List<Person> list = new ArrayList <>(); list.add(new Person (21 ,"Tom" )); list.add(new Person (23 ,"jira" )); list.add(new Person (29 ,"Jar" )); Stream<Person> stream = list.stream(); Stream<Person> personStream = list.parallelStream(); }
1 2 3 4 5 6 7 8 9 @Test public void test2 () { int [] arr = new int []{1 ,5 ,67 ,27 ,86 ,23 }; IntStream stream = Arrays.stream(arr); }
1 2 3 4 5 6 7 @Test public void test3 () { Stream<Integer> integerStream = Stream.of(1 , 23 , 543 , 62443 , 262 ); }
1 2 3 4 5 6 7 8 9 10 @Test public void test4 () { Stream.iterate(0 ,t -> t+2 ).limit(10 ).forEach(System.out::println); Stream.generate(Math::random).limit(10 ).forEach(System.out::println); }
中间操作 一个中间操作链,对数据源的数据进行处理
筛选和分片
1 2 3 4 5 6 7 8 filter(Predicate p); distinct(); limit(long maxSize); skip(long n);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test5 () { Person p = new Person (29 , "Jar" ); List<Person> list = new ArrayList <>(); list.add(new Person (21 ,"Tom" )); list.add(new Person (23 ,"jira" )); list.add(p); list.add(p); list.stream().filter(e -> e.getAge() >25 ).forEach(System.out::println); System.out.println("*************************************" ); list.stream().limit(2 ).forEach(System.out::println); System.out.println("*************************************" ); list.stream().skip(1 ).forEach(System.out::println); System.out.println("*************************************" ); list.stream().distinct().forEach(System.out::println); }
映射
1 2 3 4 5 6 7 8 9 10 11 12 map(Function f); mapToDouble(ToDoubleFunction f); mapToInt(ToIntFunction f); mapToLong(ToLongFunction f); flatMap(Function f);
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test6 () { List<String> list = Arrays.asList("aa" , "bb" , "Cc" , "dD" ); list.stream().map(str -> str.toLowerCase()).forEach(System.out::println); Stream<Stream<Character>> streamStream = list.stream().map(StreamApiTest::fromStringToStream); streamStream.forEach(stream -> { stream.forEach(System.out::println); }); list.stream().flatMap(StreamApiTest::fromStringToStream).forEach(System.out::println); }
排序
1 2 3 4 sorted(); sorted(Comparator com);
1 2 3 4 5 6 7 8 @Test public void test7 () { List<Integer> list = Arrays.asList(1 , 23 , 41 , 5 , 134 , 56 , 76 , 521 , -312 , 66 , 3 , 32 , -9 , -34 ); list.stream().sorted().forEach(System.out::println); list.stream().sorted((t1,t2) -> -t1.compareTo(t2)).forEach(System.out::println); }
终止操作(终端操作) 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会在被使用
Optional类 Optional<T>
类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值得存在。或者仅仅保存null ,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
创建Optional类对象的方法
1 2 3 4 5 6 Optionnal.of(T t); Optionnal.empty(); Optionnal.ofNullable(T t);
判断Optional容器中是否包含对象
1 2 3 4 boolean isPresent () ;void ifPresent (Consumer<? super T> consumer) ;
获取Optional容器的对象
1 2 3 4 5 6 7 T get () ; T orElse (T other) ; T orElseGet (Supplier<? extends T> other) ;