Java进阶篇

< 那些肌肉记忆:数据类型、数组、对象与类、方法、字符串、逻辑语法、运算符、修饰符、三大特性、集合、多线程与进程、IO、泛型… >

基础

Java代码实现一次编写、到处运行?

一般来说,程序运行前,会将java源代码也就是.java经过编译器转换成字节码.class,而JVM可以将字节码转换成特定平台下的机器码,所以,只要在特定平台安装对应的JVM就可以实现跨平台。

Java的三大特性

(1) 封装:对于现实中的某一事物,可以抽象出属性和方法,然后封装成一个独立的实体,对外只暴露一些对外接口,这样做的好处是可以做到专业的分工,需要的时候只需要调用即可,还可以做到隐藏信息。

(2) 继承

一个类继承另一个类,被继承的称为父类,继承类叫子类。

继承父类的属性和方法,实现代码的复用,还可以写自己特有的属性和方法,实现功能的拓展,父类也可以覆写父类的方法,即方法的重写。

但在继承过程中,父类的私有属性和构造方法是不能被继承的,且java中一个类只能继承一个父类,若要实现多继承,可以有多个接口。

判断继承关系:is-a关系

(3) 多态

在处理类型的层次结构时,经常想把一个对象不当作它所属特定类型来对待,而是将其当作其基类的对象来对待。

这使得人们可以写出不依赖于特定类型的代码。

Java的多态是基于封装和继承的,子类继承父类,以父类的身份出现,但调用方法时,可以由自身特征表现出不同的的方法行为。但子类自己特有的属性和行为不能使用。

类与对象

对象是指在现实世界中一切可以被数值化的事物,Booch提出了一个更简洁的描述:对象具有状态、行为和标识。

类是描述了具有相同特性(数据元素)和行为(功能)的对象集合。

(源自Smalltalk的五个基本特征)

重载和重写的区别

重载是指在同一个类中同名的方法,允许参数个数和类型不同,表现出不同的行为特征;
重写是指子类继承父类时,需要重写父类相同名称的方法并保持相同的参数类型和数量。

Java序列化、反序列化

Java序列化就是指将对象转换为字节序列的过程,反序列化是指将字节序列转换成目标对象的过程。

Java访问权限

Java语言为我们提供了三种访问修饰符,即private、protected、public,在使用这些修饰符修饰目标时,一共可以形成四种访问权限,即private、default、protected、public,注意在不加任何修饰符时为default访问权限。

在修饰成员变量/成员方法时,该成员的四种访问权限的含义如下:

  • private:该成员可以被该类内部成员访问(类的创建者以及内部方法),但不能被继承的子类访问;
  • default:该成员可以被该类内部成员访问,;
  • protected:该成员可以被该类内部成员访问,还可以被它的子类访问;
  • public:该成员可以被任意包下,任意类的成员进行访问。在修饰类时,该类只有两种访问权限,对应的访问权限的含义如下:
  • default:该类可以被同一包下其他的类访问;
  • public:该类可以被任意包下,任意的类所访问。

在修饰包访问权限时,类可以访问在同一个包的其他类的成员,但是在包之外,这些成员如同指定了private

Java的数据类型

Java数据类型包括基本数据类型和引用数据类型两大类。

基本数据类型有8个,可以分为4个小类,分别是整数类型(byte/short/int/long)、浮点类型(float/double)、字符类型(char)、布尔类型(boolean)。

引用类型就是对一个对象的引用,根据引用对象类型的不同,可以将引用类型分为3类,即数组、类、接口类型。

  • byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1。
  • short:2字节(16位),数据范围是 -2^15 ~ 2^15-1。
  • int:4字节(32位),数据范围是 -2^31 ~ 2^31-1。
  • long:8字节(64位),数据范围是 -2^63 ~ 2^63-1。
  • float:4字节(32位),数据范围大约是 -3.410^38 ~ 3.410^38。
  • double:8字节(64位),数据范围大约是 -1.810^308 ~ 1.810^308。
  • char:2字节(16位),数据范围是 \u0000 ~ \uffff。
  • boolean:Java规范没有明确的规定,不同的JVM有不同的实现机制。
Object有哪些公用方法

Object是所有类的父类,任何类都默认继承Object
clone保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
equals 在Object中与==是一样的,子类一般需要重写该方法。
hashCode该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
getClass final方法,获得运行时类型
wait使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait(方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生

1、其他线程调用了该对象的notify方法。

2、其他线程调用了该对象的notify All方法。3、其他线程调用了interrupt中断该线程。4、时间间隔到了。
5、此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。notify 唤醒在该对象上等待的某个线程。
notifyAll唤醒在该对象上等待的所有线程。
toString转换成字符串,一般子类都有重写,否则打印句柄。

hashCode()和equals()的区别

下边从两个角度介绍了他们的区别:一个是性能,一个是可靠性。他们之间的主要区别也基本体现在这里。
1.equals()既然已经能实现对比的功能了,为什么还要hashCode()呢?
因为重写的equals ()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。
2.hashCode()既然效率这么高为什么还要equals()呢?
因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题)﹐所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出︰

equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。
hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。

static关键字

(1)抽象的(abstract)方法是否可同时是静态的(static)?
抽象方法将来是要被重写的,而静态方法是不能重写的,所以这个是错误的。

⑵是否可以从一个静态(static)方法内部发出对非静态方法的调用?
不可以,静态方法只能访问静态成员,非静态方法的调用要先创建对象。

(3) static可否用来修饰局部变量?
static不允许用来修饰局部变量

(4)内部类与静态内部类的区别?
静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法;
普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。
如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。
如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。
(5) Java中是否可以覆盖(override)一个private或者是static的方法?
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

泛型

Java 泛型(generics)的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

  1. 泛型方法——E

    //每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。
    //一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
    //类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
    //泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型。
    
        private static <E> void printArray(E[] inputArray ){
            // 输出数组元素
            for ( E element : inputArray ){
                System.out.printf( "%s ", element );
            }
            System.out.println();
        }
    
    1. 泛型类——T

      public class generics_class<T> {
          private T t;
          public T getT(){
              return this.t;
          }
          public void setT(T t){
              this.t =t;
          }
      
          public static void main(String[] args) {
              generics_class<Integer> integer_class = new generics_class<>();
              generics_class<String> string_class = new generics_class<>();
              integer_class.setT(new Integer(10));
              string_class.setT(new String("hello world"));
              System.out.println(integer_class.getT());
              System.out.println(string_class.getT());
          }
      }
      
      1. 类型通配符

            //类型通配符一般是使用 ? 代替具体的类型参数
            private static  void data(List<?> data){
                System.out.println(data.get(0));
            }
        

集合

https://www.runoob.com/java/java-collections.html

Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayListLinkedListHashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

  • **接口:**是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
  • **实现(类):**是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
  • **算法:**是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。

集合框架的类和接口均在java.util包中。

任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。

List

ArrayList E 为引用类型 ArrayList可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,可以添加或删除元素。

  • .add(value)添加
  • get(index)获取
  • indexOf(value)获取索引
  • .lastIndexOf(value)获取最后一次索引
  • .set(index,value)修改
  • .remove(index)删除
  • .clear()清空
  • .sort()排序
  • .size()计算大小

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。

栈或队列可以使用链表代替存储结构。

与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。

  • .addFirst(value)
  • removeFirst(value)
  • .getFirst(value)表头操作
  • .addLast(value)
  • removeLast(value)
  • .getLast(value)表尾操作
public class list {
    public static void main(String[] args) {
        arrayList(); //ArrayList
        linkedList(); //LinkedList
    }
    //arrayList
    private static void arrayList(){
        List<String> list = new ArrayList<String>();
        list.add("hello");list.add("world");list.add("welcome");
        //普通遍历
        for (String str:list){ System.out.println(str); }
        //使用迭代器遍历,该方法可以不用担心在遍历的过程中会超出集合的长度
        Iterator<String> iterator = list.iterator();
        while(iterator.hasNext()){ System.out.println(iterator.next()); }
    }
    
    //linkedList
    private static void linkedList(){
         *ArrayList类似 ,效率不一样
         */
        LinkedList<String> list = new LinkedList<>();
        list.add("hello");list.add("world");list.add("welcome");
    }
}

Map
  • .get(key)获取key对应的value
  • .put(key,value)
  • .keySet(): ==>返回的是map的key集合 通过get(key)获得value
  • .entrySet() =>返回的是 Map.Entry<String, String>集合 含有方法:.getKey() .getValue()
  • .values() =>返回的是所有的value,但不能遍历key
public class map {
    public static void main(String[] args) {

        //Map遍历
        Map<String, String> map = new HashMap<>();
        map.put("1","value1");map.put("2","value2");map.put("3","value3");

        System.out.println("使用keySet()遍历");
        for(String key : map.keySet()){
            System.out.println(key+":"+map.get(key));
        }

        System.out.println("通过Map.entrySet使用iterator遍历key和value:");
        Iterator<Map.Entry<String,String>> iterator = map.entrySet().iterator();
        while(iterator.hasNext()){
            Map.Entry<String, String> next = iterator.next();
            System.out.println(next.getKey()+":"+next.getValue());
        }

        System.out.println("推荐!!!通过Map.entrySet遍历key和value");
        for (Map.Entry<String,String> entry:map.entrySet()){
            System.out.println("key:"+entry.getKey()+"value:"+entry.getValue());
        }
    }
}

      /**
         * HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
         * HashSet 允许有 null 值。
         * HashSet 是无序的,即不会记录插入的顺序。
         * HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。
         * 必须在多线程访问时显式同步对 HashSet 的并发访问。
         */
Queue
//队列,先进先出
Queue<Integer> queue = new LinkedList<Integer>();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            queue.offer(random.nextInt(i+10)); //头部插入
        }
        queue.poll();  //返回队头并删除,空时返回null remove空时返回异常
        System.out.println(queue.peek()); //返回队头不删除,空时返回null,element空时返回异常

Set和List的区别

  • Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
  • Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
  • List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 。

ArrayList和Vector异同点

ArrayList和Vector在很多时候都很类似。
(1)两者都是基于索引的,内部由一个数组支持。
(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。(3)ArrayList和Vector的迭代器实现都是fail-fast的。
(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。

以下是ArrayList和Vector的不同点
(1) Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。
(2)ArrayList比 Vector快,它因为有同步,不会过载。
(3) ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

Array和ArrayList区别

Array可以存储基本数据类型和对象,ArrayList 只能存储对象。
Array是指定固定大小的,而ArrayList大小是自动扩展的。
Array内置方法没有ArrayList 多,比如addAll、removeAll、iteration等方法只有ArrayList有。

IO

  1. 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
  2. 按照单位大小:字符流、字节流。按照流的方向:输出流、输入流。
  3. 不同的流(管道、文件、缓存)使用不同的类来输入输出
  4. 不同的数据类型(字节流、字符流)使用不同的类获取(InputStream、Reader)

请添加图片描述

基础教程文档:

https://www.runoob.com/java/java-files-io.html

JDK 文档:

https://tool.oschina.net/apidocs/apidoc?api=jdk-zh

实例1打开一个文本文件,每次读取一行内容,存储在LinkedList中,并逆序打印出来:(使用字符流)

        BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\Eric\\Desktop\\iotest.txt"));  //字符流
        String line;
        LinkedList<String> linkedList = new LinkedList<>();
        while((line = bufferedReader.readLine())!=null){
            linkedList.add(line);
        }
        bufferedReader.close();
        for (String s : linkedList) {
            System.out.println(s);
        }

实例2:文件传输(使用字节流)

        String rImage = tempMage;  //临时文件名
        File userImage = new File(fileUploadPath+uid+File.separator +rImage);  //指定用户文件夹及文件名字
        File parentFile = userImage.getParentFile();
        if(!parentFile.exists()){
            parentFile.mkdirs();
        }
        FileInputStream fileInputStream = new FileInputStream(fileTempPath + tempMage);//字节输入流
        FileOutputStream fileOutputStream = new FileOutputStream(userImage); //字节输出流
        byte[] buffer = new byte[100];
        int hasRead = 0;
        while((hasRead = fileInputStream.read(buffer))>0){
            fileOutputStream.write(buffer,0,hasRead);
        }
        System.out.println(userImage.getAbsolutePath());
        fileInputStream.close();
        fileOutputStream.close();
    }

实例3:多线程中缓存输出避免阻塞

 		Map map = new HashMap<Integer,Object>();
        Process proc;
        System.out.println("thread is trying to call python.py:");
    //    proc = Runtime.getRuntime().exec("python /root/zhiyuan/test.py");// 执行py文件
//        proc = Runtime.getRuntime().exec("cmd /c conda activate pytorch&&d:&&python "+algorithmYolo5Path+"detect.py --source "+imageTempPath+detectImage);// 执行py文件
//        proc = Runtime.getRuntime().exec("/bin/sh -c cd /www/server&&python "+algorithmYolo5Path+"detect.py --source "+imageTempPath+detectImage);// 执行py文件
        proc = Runtime.getRuntime().exec("/root/anaconda3/envs/pytorch/bin/python3.7 "+algorithmYolo5Path+"detect.py --source "+imageTempPath+detectImage);// 执行py文件

        try (InputStreamReader ir = new InputStreamReader(proc.getInputStream());
             InputStreamReader er = new InputStreamReader(proc.getErrorStream());
             LineNumberReader input = new LineNumberReader(ir);
             LineNumberReader error = new LineNumberReader(er)) {

            new Thread(() -> {
                try {
                    String line;
                    int index = 0;
                    while ((line = input.readLine()) != null) {
                        System.out.println("标准输入:"+line);
                        map.put(index,line);
                        index++;
                    }
                } catch (Exception e) {
                }
            }).start();

            new Thread(() -> {
                try {
                    String line;
                    while ((line = error.readLine()) != null) {
                        System.out.println("错误输入"+line);
                    }
                } catch (Exception e) {
                }
            }).start();
            proc.waitFor();
            return map;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭process");
            proc.destroy();
        }
        return map;

字符串

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
String是不可变的,即定义对象后,若对其做了修改,将产生新的一个实例。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 的方法不是线程安全的(不能同步访问)。

 StringBuilder sb = new StringBuilder(10);
 sb.append("Runoob..");
 sb.insert(8, "Java");
 sb.delete(5,8);
//结果:RunooJava!
//还有 replace() reverse() substring()

//常用的String类方法:
length()	equals()	compareTo()		startsWith()	endsWith()	substring(左闭右开)
concat()	replace()	roLowerCase()	toUpperCase()	trim()	valueOf()	split()

然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

  StringBuffer sBuffer = new StringBuffer("StringBuffer");

进程与线程

什么是进程
进程是系统中正在运行的一个程序,程序一旦运行就是进程。进程可以看成程序执行的一个实例。进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。一个进程无法访问另一个进程的变量和数据结构,如果想让一个进程访问另一个进程的资源,需要使用进程间通信,比如管道,文件,套接字等。

什么是线程
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中何以并发多个线程,每条线程并行执行不同的任务。

线程状态转换图

请添加图片描述

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

线程创建:

//方法一:继承Thread类,重写run(),调用start()函数开启线程
public class Thread1_Thread extends Thread {
    @Override
    public void run() {
      //
    }

    public static void main(String[] args) {
        //创建一个线程对象并启动
        Thread1_Thread thread1Thread = new Thread1_Thread();
        thread1Thread.start();
        //
    }
}

//方法二:继承runnable接口
public class Thread2_Runnable implements Runnable {
    //override
    public void run() {
    //
    }

    public static void main(String[] args) {
        //创建一个runnable接口的实现类对象
        Thread2_Runnable thread2Runnable = new Thread2_Runnable();
        //创建线程对象,通过线程对象启动我们的线程,代理模式
        new Thread(thread2Runnable).start();
		//
    }
}

一些常用的方法

//.join() 可以实现合并线程,实际即为“插队”,使用join将强行从主线程调到子线程去,此时为blocked状态
//.yield() 线程礼让,但是否礼让成功看CPU 
// .getPriority()可以获得优先级的计数,setPriority(int xx)设置优先级
//线程只能启动一次 可以使用 getState返回线程状态:创建状态、可运行状态、等待状态、阻塞状态、消亡状态
// Thread.sleep(1000);
//.currentThread().getName()  获取当前线程名称
//callable ---- call() 带有返回值的多线程
//.setDaemon() 设置为后台进程,需要在.start()之前

#线程协作
waiting() 线程挂起
notify() 或者 notifyAll() 线程恢复执行

sleep和yield都不释放锁,waiting挂起会释放锁

并发

//并发:同一个对象被多个线程同时操作
//同步的安全性需要两个条件:队列+锁
//synchronized(Obj){方法块} 对象Obj应该是增删改的对象 默认this
* 死锁:两个及两个以上的对象因为缺少对方手上的已获得且未释放的资源而阻塞产生的死循环现象
* 产生死锁的条件:
* 1.互斥条件:一个资源每次只能被一个进程使用。
* 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
* 3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
* 4.循环等待条件:若干个进程之间形成一种头尾相接的循环等待资源关系
* 避免死锁的条件:欲将取之,必先与之
package Thread;
public class BuyTicket {
    public static void main(String[] args) {
        Counter station = new Counter();
        for (int i = 0; i < 20; i++) {
            new Thread(station, String.valueOf(i)).start();
        }
    }
}
class Counter implements Runnable {
    private int tickets = 18;
    @Override
    public void run() {
        //买票
//        if (tickets>0) {
                try {
                    Thread.sleep(1000);
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//    }
    //锁buy(),对应的对象 Couter 因为资源在Couter里面
    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (tickets <= 0) {
            System.out.println(Thread.currentThread().getName() +"没票了");
            return;
        }
        //判断是否已买票
            System.out.println(Thread.currentThread().getName() + "拿到了" + tickets--);
    }
}

JUC锁

/*JUC的lock机制与synchronized的异同:
 * 1.相对于隐性锁synchronized,lock是显性的,需要手动开启和关闭,且若有异常,unlock需要写在finally里面
 * 2.lock锁只有代码块锁,synchronized有代码块锁和方法锁
 * 3.使用lock锁拓展性更强(提供更多的子类)
 * */

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Counter station = new Counter();
        new Thread(station, "张三").start();
        new Thread(station, "李四").start();
        new Thread(station, "黄五").start();
    }
}


class Counter implements Runnable {
    private int tickets = 10;
    boolean flag = true;
    //定义锁
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        //买票
        while (flag) {

            try {
                lock.lock();//加锁
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                lock.unlock();
            }

        }
    }

    //锁buy(),对应的对象 Couter 因为资源在Couter里面
    private void buy() throws InterruptedException {
        //判断是否有票
        if (tickets <= 0) {
            flag = false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "拿到了" + tickets--);
    }
}

协作

package Thread;

//司机  售票员 大巴,事件:司机关门后才能售票,到站后司机会开门,假设15个站,设置通行,完成大巴运作
//信号灯法
public class TestDC {
    public static void main(String[] args) {
        Travel travel = new Travel();
        new Bus(travel).start();
        new Conductor(travel).start();
    }
}


class Bus extends Thread {
    Travel travel;

    public Bus(Travel travel) {
        this.travel = travel;
    }

    @Override
    public void run() {
        for (int i = 0; i < 15; i++) {
            try {
                travel.stop("第" + (i + 1) + "站");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Conductor extends Thread {
    Travel travel;

    public Conductor(Travel travel) {
        this.travel = travel;
    }

    @Override
    public void run() {
        for (int i = 0; i < 15; i++) {
            try {
                travel.sell("第" + (i + 1) + "站");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//class Passenger{
//    int id;
//    public Passenger(int id){this.id=id;}
//}

class Travel {
    //Passenger[] passenger = new Passenger[15];
    String station;
    Boolean flag = true;   //如果为真 则代表到站开门,如果为假则代表关门售票

    public synchronized void stop(String station) throws InterruptedException {
        if (!flag) {
            this.wait();
        }
        System.out.println(station + "到了;");
        this.notifyAll();
        this.flag = !this.flag;
    }

    public synchronized void sell(String station) throws InterruptedException {
        if (flag) {
            this.wait();
        }
        System.out.println(station + "售票。");
        this.notifyAll();
        this.flag = !this.flag;
    }
}
package Thread;

//生产者 消费者 产品 缓冲区
/*线程通行
 * wait() 表示线程一直等待,直到其他线程通知,与sleep的区别是此时会释放锁
 *notifyAll() 唤醒其他线程
 * */
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread {
    SynContainer container;

    public Productor(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                container.push(new Chicken(i)); //id=i
                System.out.println("生产了" + i + "只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//消费者
class Consumer extends Thread {
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费了" + container.pop().id + "只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//产品
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer {
    //容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) throws InterruptedException {
        //如果容器满了,等待消费者消费
        if (count == chickens.length) {
            //通知消费者消费,生产者等待
            this.wait();
        }
        //如果没有满,继续生产
        chickens[count] = chicken;
        count++;
        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop() throws InterruptedException {
        //判断能否消费
        if (count == 0) {
            //等待生产者生产,消费者等待
            this.wait();
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        //吃完了,通知消费者生产
        this.notifyAll();
        return chicken;

    }
}

异常和反射

error和exception有什么区别?
error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。
exception表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题。

说出5个常见的RuntimeException?
(1)Java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。
(2)Java.lang.NumberFormatException字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。
(3)Java.lang.IndexOutOfBoundsException数组角标越界异常,常见于操作数组对象时发生。(4)Java.lang.IllegalArgumentException方法传递参数错误。
(5)Java.lang.ClassCastException数据类型转换异常。

throw和throws的区别?
throw :
(1)throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
(2)throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常。
throws:
(1)@throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。
(2)throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。
(3)throws表示出现异常的一种可能性,并不一定会发生这种异常。

什么是Java反射机制?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

举例什么地方用到反射机制?
1.JDBC中,利用反射动态加载了数据库驱动程序。
2.Web服务器中利用反射调用了Sevelet的服务方法。
3.Eclispe等开发工具利用反射动态创析对象的类型与结构,动态提示对象的属性和方法。
4.很多框架都用到反射机制,注入属性,调用方法,如Spring。

java反射机制的作用
在运行时判定任意一个对象所属的类
在运行时构造任意一个类的对象;
在运行时判定任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理;

利用反射创建对象
1.通过一个全限类名创建一个对象
Class.forName(“全限类名”);例如: com.mysql.jdbc.Driver Driver类已经被加载到jyvm中,并且完成了类的初始化工作就行了
类名.class;获取Class<? >clz对象对象.getClass();
⒉获取构造器对象,通过构造器new出一个对象Clazz.getConstructor([String.class]);
Con.newInstance([参数]);
3.通过class对象创建一个实例对象(就相当与new类名()无参构造器)Cls.newInstance();

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值