1、进程与线程
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
在JAVA中实现多线程有两种途经:
- 继承Thread类
- 实现Runnable接口(Callable接口)
继承Thread类实例代码:
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(this.name+"---->"+i);
}
}
}
public class ThreadStudyTest {
public static void main(String[] args) {
MyThread m1 = new MyThread("线程A");
MyThread m3 = new MyThread("线程B");
MyThread m2 = new MyThread("线程C");
m1.start();//启动线程
m2.start();
m3.start();
}
}
利用Runnable来实现多线程,
class MyRunnableThread implements Runnable{
private String name;
public MyRunnableThread(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程:"+this.name+"---->"+i);
}
}
}
public class RunnableThreadTest {
public static void main(String[] args) {
new Thread(new MyRunnableThread("A")).start();
new Thread(new MyRunnableThread("B")).start();
new Thread(new MyRunnableThread("C")).start();
}
}
注意Runnable中只有一个方法run();如果要启动线程,需要借助Thread类来完成,只有Thread类有线程操作的所有方法。
两者实现的区别
- Thread类是Runnable接口的子类,使用Runnable接口实现多线程能够避免单继承局限
- Runnable接口实现的多线程可能比Thread类更加方便数据共享
第三种实现方式:Callable,
由于Runnable接口实现可以避免单继承的局限,但是不能返回一个操作结果。为了解决这个问题,提供了一个Callable接口,但是这个接口操作起来难度大一些。
这个接口是在java.util包中,而Runnable是在java.lang包中,如下图是Callable接口:
从上图中就可以看出返回的结果类型是在声明类的时候定义。
线程休眠方法:Thread.sleep(1000L); 是线程处于休眠状态,但是资源没有释放。
线程优先级:越高的优先级,有可能越先执行。在JAVA中,对线程优先级操作提供了两个方法:
- 设置优先级:publlic final void setPriority(int newPriority);
- 取得优先级:public final int getPriority();
对于优先组的取值都是使用Int数据类型,对于此数据类型在Thread类中设置了三种取值:
- 最高优先级:public static final int MAX_PRIORITY;对应的常量是:10
- 中等优先级:public static final int NORM_PRIORITY;对应的常量 是:5
- 最低优先级:public static final int MIN_PRIORITY;对应的常量是:1
2、Runtime类
Runtime类是一个单例模式的类,通过Runtime.getRuntime()来获取Runtime类,那么Runtime类主要 作用是:
Runtime类是直接与本地运行的所有相关属性的集合,主要的方法有:
- 返回所有的可用内存:public long totalMemory();
- 返回最大可用内存:public long maxMemory();
- 返回空余内存空间:public long freeMemory();
public class RuntimeTest {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
System.out.println(rt.maxMemory());
System.out.println(rt.totalMemory());
System.out.println(rt.freeMemory());
}
}
Runtime类可以调用本机的可执行程序,并且创建进程;
执行程序的方法:public Process exec(String command) throws IOException
如下代码是调用本机的画图程序:
public class RuntimeTest {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
/* System.out.println(rt.maxMemory());
System.out.println(rt.totalMemory());
System.out.println(rt.freeMemory());*/
try {
Process ps = rt.exec("mspaint.exe");
Thread.sleep(2000L);
ps.destroy();//关掉执行程序
} catch (IOException e) {
e.printStackTrace();
}
}
}
Runtime类提供了gc()方法,可以调用垃圾回收器,操作内存。
3、final finally finalize这三者有什么区别?
- final:是关键字,定义不能继承的类、不能被覆写的方法和常量。
- finally:是关键字,异常的统一出口
- finalize:是Object类提供的一个方法(protected void finalize() throws Throwable),指的是对象收回的方法,即使出现了异常也不到于程序中断。
4、对象克隆
对象克隆就是对象的复制操作,在Object类里面提供有一个专门的克隆方法,此方法会抛出一个“CloneNotSupportedException”异常。如下代码:
class Book2 implements Cloneable {
private String titel;
private double price;
public Book2(String titel, double price) {
this.titel = titel;
this.price = price;
}
public String getTitel() {
return titel;
}
public void setTitel(String titel) {
this.titel = titel;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "书名:" + this.titel + ":价格是:" + this.price;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class CloneTest {
public static void main(String[] args) {
Book2 ba = new Book2("JAVA开发",23.4);
Book2 bb = null;
try {
bb = (Book2) ba.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
bb.setTitel("Python");
System.out.println(ba.toString());
System.out.println(ba.toString());
}
}
注意:Object类中的clone()方法是protected,需要子类进行重写,并把protected改成public,子类才能调用。
5、Math、Random、BigInteger、
Math类就是专门用于数学计算的操作类,里面提供了一系列的数学计算方法。
在Math类里面提供的一切方法都是static型的方法,因为Math类里面没有普通属性。
Random类是取得随机数的操作类。
大数字操作类:BigInterger、BigDecimal 这两个,当double存放不了数据可以用这个两个类中进行操作,但是JAVA本身提供的这两个大数字操作类相对来说比较简单,如果有大数字的复杂计算需求,就需要找专门的第三方工具。
BigDecimal类是可以保存小数的,可以进行精确的四舍五入操作,可以保留多位小数,而Math.round()是四舍五入之后,全部是整型了。
下面是Math、Random、BigInteger、Decimal这四个类的重要方法的代码:
class MyMath{
public static double divide(double d,int scale){
BigDecimal big = new BigDecimal(d);
BigDecimal big2 = new BigDecimal(1);
return big.divide(big2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
public class ImportantClassTest {
public static void main(String[] args) {
System.out.println(Math.PI);
System.out.println(Math.round(15.5));//16
System.out.println(Math.round(-15.5));//-15
System.out.println(Math.round(-15.51));//16
Random ran = new Random();
for (int i = 0; i <10 ; i++) {
System.out.print(ran.nextInt(100)+" ");
}
System.out.println();
BigInteger big1 = new BigInteger("379646476738678346");
BigInteger big2 = new BigInteger("78896768678");
System.out.println(big1.add(big2));
System.out.println(big1.subtract(big2));
System.out.println(big1.multiply(big2));
System.out.println(big1.divide(big2));
System.out.println(big1.divideAndRemainder(big2));
System.out.println(MyMath.divide(23.44555666,2));
}
}
6、Date、Calendar、SimpleDateFormat三个类的关系
这三个类是对日期时间数据进行转换和使用。
其中Date的构造有两种:
- 无参构造:public date();
- 有参构造 :public date(long date);
- 转换为long型:public long getTime();
public class DateClassTest {
public static void main(String[] args) {
long dateLong = System.currentTimeMillis();
Date date = new Date();
Date date2 = new Date(dateLong);
System.out.println(date);
System.out.println(date2);
}
}
SimpleDateFormat类主要解决日期格式化操作
- 构造方法 public SimpleDateFormat(String pattern);
- 将Date转换为String:public final String format(Date date );
- 将String转换为Date:public Date parse(String source) throws parseException;
Calendar类主要是进行简单的日期计算使用,如下代码:
public class DateClassTest {
public static void main(String[] args) {
/* long dateLong = System.currentTimeMillis();
Date date = new Date();
Date date2 = new Date(dateLong);
System.out.println(date);*/
Calendar c = Calendar.getInstance();
StringBuffer sb = new StringBuffer();
sb.append(c.get(Calendar.YEAR)+"-");
sb.append(c.get(Calendar.MONTH)+1+"-");
sb.append(c.get(Calendar.DAY_OF_MONTH)+" ");
sb.append(c.get(Calendar.HOUR_OF_DAY)+":");
sb.append(c.get(Calendar.MINUTE)+":");
sb.append(c.get(Calendar.SECOND));
System.out.println(sb.toString());
}
}
7、比较器的使用
主要介绍Arrays类、两种比较器的使用你以及数据结构(二叉树)。
Arrays类:在这个类中有一个方法存在二分查找法:
public static int binarySearch();
要实现二分查找法,首先数组中必须是经过排序的数据,才能进行二分查的,效率是非常高,如下代码:
public class ComparableClassTest {
public static void main(String[] args) {
int[] array = new int[]{1,2,3,4,9,6,5,8,0};
//使用二分查找法,数组是必须先排序
Arrays.sort(array);
//对于二分查找法,如果能查到数据,返回它在数组中的索引,否则返回负值
System.out.println(Arrays.binarySearch(array,10));
}
}
Arrays中有一个方法是把数组转换成字符串来输出:public static String toString(int[] a);
比较器:Comparable
在Arrays类中有一个方法可以实现对象数组排序:public static void sort(Object a);是因为数组中的对象都实现了Comparable接口。例如下面代码:
class BookComparable implements Comparable<BookComparable>{
@Override
public int compareTo(BookComparable o) {
if (this.price >o.price) {
return 1;
}else if(this.price < o.price){
return -1;
}else {
return 0;
}
}
private String titel;
private double price;
public BookComparable(String titel, double price) {
this.titel = titel;
this.price = price;
}
public String getTitel() {
return titel;
}
public void setTitel(String titel) {
this.titel = titel;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return
"titel='" + titel + '\'' +
", price=" + price ;
}
}
public class ComparableClassTest {
public static void main(String[] args) {
/* int[] array = new int[]{1,2,3,4,9,6,5,8,0};
//使用二分查找法,数组是必须先排序
Arrays.sort(array);
//对于二分查找法,如果能查到数据,返回它在数组中的索引,否则返回负值
System.out.println(Arrays.binarySearch(array,10));*/
BookComparable book = new BookComparable("JAVA",87.8);
BookComparable book2 = new BookComparable("JAVA2",98.8);
System.out.println(book.compareTo(book2));
}
}
注意:对于对象数组进行排序,是直接调用Comparable接口的方法,数组中的对象必须进行实现。
比较器:Comparator
Comparator是一个函数式接口,如下代码:
class BookComparable implements Comparable<BookComparable>{
@Override
public int compareTo(BookComparable o) {
if (this.price >o.price) {
return 1;
}else if(this.price < o.price){
return -1;
}else {
return 0;
}
}
private String titel;
private double price;
public BookComparable(String titel, double price) {
this.titel = titel;
this.price = price;
}
public String getTitel() {
return titel;
}
public void setTitel(String titel) {
this.titel = titel;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return
"titel='" + titel + '\'' +
", price=" + price ;
}
}
class BookComparator implements Comparator<BookComparable> {
@Override
public int compare(BookComparable o1, BookComparable o2) {
if (o1.getPrice() >o2.getPrice()) {
return 1;
}else if(o1.getPrice() < o2.getPrice()){
return -1;
}else {
return 0;
}
}
}
public class ComparableClassTest {
public static void main(String[] args) {
/* int[] array = new int[]{1,2,3,4,9,6,5,8,0};
//使用二分查找法,数组是必须先排序
Arrays.sort(array);
//对于二分查找法,如果能查到数据,返回它在数组中的索引,否则返回负值
System.out.println(Arrays.binarySearch(array,10));*/
BookComparable books [] = new BookComparable[] { new BookComparable("JAVA",87.8)
, new BookComparable("JAVA2",98.8)};
Arrays.sort(books,new BookComparator());
System.out.println(Arrays.toString(books));
}
}
说明:两种比较器(Comparable、Comparator)区别
- 如果对象数组要进行排序,那么必须设置排序规划,可以使用Comparable或Comparator接口都可以
- java.lang.Comparable是一个类定义的时候实现好的接口,这样对象数组就可以进行排序,在Comparable接口下定义有一个public int compareTo()方法
- java.util.Comparetor是专门定义一个指定类的比较规则,属于挽救的比较操作,里面有两个方法:public int compare();public int equal();
我们经常使用的是Comparable接口。
8、正则表达式
在所有的开发一定有有正则的支持,记下常用的正则标记以及掌握String类对正则的支持。
正则表达式是对字符串进行操作,正则是从JDK 1.4之后正式引入到JAVA工具中,所有正则支持的类都在java.util.regex包中。
在java.util.regex中定义了两个类:
- Pattern类:此类对象要想取得,必须调用compile()方法,这个方法是用来编译正则类。
- Matcher类:通过Pattern类取得
正则标记:
1、单个字符:(数量:1)
- 字符:表示由一个字符组成
- \\:表示转义字符"\"
- \t:表示tab
- \n:表示换号
2、字符集:(数量:1)
- [abc]:表示可能是字符a或者字符b或者是字符c中的任意一个。
- [^abc]:表示不是abc中的任意一位。
- [a-z]:所有的小定字母
- [A-Za-z]:表示所有的大小写字母
- [0-9]:表示任意一位数字
3、简化的字符集表达式:(数量:1)
- .:表示任意一个字符,包括数字、大小写字母以及各种特殊字符
- \d:等价于[0-9],是简化写法
- \D:等价于[^0-9],也是简化写法
- \s:表示任意的空白字符,例如:空格(\t)、回车(\n)
- \S:表示任意的非空白字符
- \w:等价于[a-z][A-Z][0-9],表示任意的字母、数字以及_下划线组成
- \W:表示上面的非
4、边界匹配:(不要在JAVA中使用,在JS中使用)
- ^:正则的开始
- $:正则的结束
5、数量表达
- 正则?:表示此正则出现0次或1次
- 正则+:表示此正则出现一次或者一次以上
- 正则*:表示此正则可以出现0次、一次或者 多次
- 正则{n}:表示此正则可以出现n次
- 正则{n,}:表示此正则可以出现n次以上(包含n次)
- 正则{n,m}:表示此正则可以出现n次和M次之间
6、逻辑运算:
- 正则1正则2:正则1判断完成之后继续判断正则2
- 正则1|正则2:表示正则1或者正则2有一组满足即可
- (正则):将多个正则作为一组,可以为这一组设置出现的次数。
String类对正则的支持:
代码如下:
public class RegulerExpressClassTest {
public static void main(String[] args) throws ParseException {
String str = "akljkdlfjiue90#$%^&@%JHKHS&D*(^&#%57136767()*&(*&*^$#$kljkljkj33&&^#JKLjklk11";
String regex = "[^a-z]";
//去掉所有的非小写字母
System.out.println(str.replaceAll(regex,""));
//下面代码是通过正则进行分组
String str2 = "kljkljklj23kljlk45kljklj6hjhjh6jhjh8hjhjh0werw3rew3r2w3er33";
String regex2 = "\\d+";
String[] splitStr = str2.split(regex2);
for (int i = 0; i < splitStr.length; i++) {
System.out.println(splitStr[i]);
}
//验证一个字符串是否是数字,如果是变成double型
String str3 = "344555.2";
String regex3 = "\\d+(\\.\\d+)?";
System.out.println(str3.matches(regex3));
if (str3.matches(regex3) ) {
System.out.println(Double.parseDouble(str3));
}
//判断是否是日期类型进行转换输出
String str4 = "2018-09-09";
String regex4 = "\\d{4}-\\d{2}-\\d{2}";
if (str4.matches(regex4)){
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(str4);
System.out.println(date.toString());
}
//判断电话号码 格式一:51283345 格式二:010-12345678 格式三:(010)-59823123
String str5 = "010-4567890";
String numberRegex = "(\\d{3,4}-)?\\d{7,8}";
System.out.println(str5.matches(numberRegex));
//使用正则判断邮箱
String str6 = "[email protected]";
String regex6 = "\\w+@\\w+\\.\\w+";
System.out.println(str6.matches(regex6));
}
}
9、反射机制
从三个方面进行讲解:首先认识反射,其次理解反射的机制,最后了解通过反射调用类的结构。
反射机制的话先通过“反"来理解,既然有"反"就有正,什么是正?
所谓“正”就是先有类再有对象。
所谓“反”就是通过对象找到对象的出处,在Object类里面提供有一个方法:
取得Class对象:public final Calss<?> getClass();
Class类对象的实例化
java.lang.Class是一个类,这个类是反射操作的源头,即,所有的反射都要从此类开始,这个类有三个实例化方法:
- 调用Object类中的getClass()方法(用的相对少一些)
public class reflexClassTest {
public static void main(String[] args) {
Date d = new Date();
Class<?> c = d.getClass();
System.out.println(c.toString());
}
}
- 使用类.class中获得
public class reflexClassTest {
public static void main(String[] args) {
// Date d = new Date();
//Class<?> c = d.getClass();
Class<?> c = Date.class;
System.out.println(c.toString());
}
}
- 调用Class类提供的一个方法
public static Class<?> forName(String className) throws ClassNotFoundException;
public class reflexClassTest {
public static void main(String[] args) throws ClassNotFoundException {
// Date d = new Date();
//Class<?> c = d.getClass();
//Class<?> c = Date.class;
Class<?> c = Class.forName("java.util.Date");
System.out.println(c.toString());
}
}
取得类对象,就要反射实例化对象
一旦拿到Class对象,就可以通过反射机制来实例化对象。
实例化对象方法:public T newInstance() throws InstantiationException,IilegalAccessException;
如下代码所示:
class BookReflex{
public BookReflex() {
System.out.println("-----------------------");
}
@Override
public String toString() {
return "这是一本书";
}
}
public class reflexClassTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> book = Class.forName("BookReflex");
Object obc = book.newInstance();
BookReflex b = (BookReflex) obc;
System.out.println(b.toString());
}
}
为什么要使用反射机制?
通过反射机制能够设计出松耦合的架构。new是耦合的最大元凶;
调用构造方法:
通过获得Constructor类来执行有参构造,默认情况下是调用无参构造,如果没有无参构造就会报异常(如果类中没有任何构造方法,就会默认是无参构造方法):NoSuchMethodException;
调用有参构造:
class BookReflex{
private String title;
private double price;
public BookReflex(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "书的名字是:"+this.title+" "+"价格是:"+this.price;
}
}
public class reflexClassTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> book = Class.forName("BookReflex");
Constructor<?> con = book.getConstructor(String.class,double.class);
Object obc = con.newInstance("这本书!",89);
BookReflex b = (BookReflex) obc;
System.out.println(b.toString());
}
}
反射调用普通方法:
普通方法是对象实例后才能调用。对象的实例化方法有三种:new方法、克隆方法、反射机制
在Class类中提供方法有:
- 取得所有的方法的对象:public Method[] getMethods();
- 取得指定方法名称的方法对象:public Method getMethod(String methodName,Class<?>...parameterTyper);
上面两个方法都是返回Method对象,那么Method对象我们关注哪些方法:
- 调用方法:public Object invoke(Object obj,Object...args) throws IilegalAccessException;
代码如下:
class BookReflex{
private String title;
private double price;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
public class reflexClassTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
String str = "title";
Class<?> book = Class.forName("BookReflex");
Object obc = book.newInstance();
Method setM = book.getMethod("set"+initMethod(str),String.class);
Method getM = book.getMethod("get"+initMethod(str));
setM.invoke(obc,"这是一本JAVA书!");
System.out.println(getM.invoke(obc));
}
public static String initMethod(String str){
return str.substring(0,1).toUpperCase()+str.substring(1);
}
}
反射调用成员:
在Class类中获得成员的方法:
- 取得全部成员:public Field[] getDeclaredFields() throws SecurityException;
- 取得指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException;
上面的代码返回的类型是Field类,这个类中有两个重要的方法
- 取得属性内容:public Object get(Object obj) ;
- 设置属性内容:public void set(Object obj,Object value);
代码如下:
class BookReflex{
private String title;
}
public class reflexClassTest {
public static void main(String[] args) throws Exception {
String str = "title";
Class<?> book = Class.forName("BookReflex");
Object obc = book.newInstance();
Field field = book.getDeclaredField(str);
//通过下面的设置,类中的私有属性也能访问(打破封装)
field.setAccessible(true);
field.set(obc,"java开发!");
System.out.println( field.get(obc));
}
}
从上面的代码,对于私有的方法可以取消封装,不过,我们很少使用,建议还是使用get与set方法。
更多JAVA高级语言的特性,请查看续集:JAVA高级语言重要概念以及重要的类之二(文件操作、字节流、字符流、转换流、缓冲流、内存流、打印流、扫描流等)