Java笔记 ·

Java基础小结(三)

Java 数据结构

Java中的数据结构主要包括以下几种接口和类:

  • 枚举(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 栈(Stack)
  • 字典(Dictionary)
  • 哈希表(Hashtable)
  • 属性(Properties)

以上这些类是传统遗留的,在Java2中引入了一种新的框架-集合框架(Collection)

枚举(Enumeration)

枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。 枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。

例如,枚举定义了一个叫nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。

位集合(BitSet)

位集合类实现了一组可以单独设置和清除的位或标志。

该类在处理一组布尔值的时候非常有用,你只需要给每个值赋值一"位",然后对位进行适当的设置或清除,就可以对布尔值进行操作了。

向量(Vector)

向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。

和数组一样,Vector对象的元素也能通过索引访问。

使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。

栈(Stack)

栈(Stack)实现了一个**后进先出(LIFO)**的数据结构。

你可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。

当你从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。

字典(Dictionary)

字典(Dictionary) 类是一个抽象类,它定义了键映射到值的数据结构。

当你想要通过特定的键而不是整数索引来访问数据的时候,这时候应该使用Dictionary。

由于Dictionary类是抽象类,所以它只提供了键映射到值的数据结构,而没有提供特定的实现。

注: Dictionary类已经过时了。在实际开发中,你可以实现Map接口来获取键/值的存储功能。

哈希表(Hashtable)

Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。

例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。

哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。

Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。

然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步

属性(Properties)

Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集。属性列表中每个键及其对应值都是一个字符串。

Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。

迭代器 iterator 用法

Java 中的 Iterator 功能比较简单,并且只能单向移动:

  • (1) 使用方法 iterator() 要求容器返回一个 Iterator。第一次调用 Iterator 的 next() 方法时,它返回序列的第一个元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 继承。
  • (2) 使用 next() 获得序列中的下一个元素。
  • (3) 使用 hasNext() 检查序列中是否还有元素。
  • (4) 使用 remove() 将迭代器新返回的元素删除。

Java 集合框架

合框架被设计成要满足以下几个目标。

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
  • 对一个集合的扩展和适应必须是简单的。

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

  • 接口:是代表集合的抽象数据类型。接口允许集合独立操纵其代表的细节。在面向对象的语言,接口通常形成一个层次。
  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构。
  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现

除了集合,该框架也定义了几个Map接口和类。Map里存储的是键/值对。尽管Map不是collections,但是它们完全整合在集合中。

Set和List的区别

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

小结

  • Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。
  • 集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。
  • 集合框架的类和接口均在java.util包中。
  • 任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。
  • Vector与ArrayList一样,也是通过数组实现的,不同的是Vector支持线程的同步

Collection与Collections的区别

  • 1、Collection是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
  • 2、Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
    提供了对集合进行排序、遍历等多种算法实现。

Vector & ArrayList 的主要区别 

1) 同步性:Vector是线程安全的,也就是说是同步的 ,而ArrayList 是线程序不安全的,不是同步的 数2。

2)数据增长:当需要增长时,Vector默认增长为原来一倍 ,而ArrayList却是原来的50%  ,这样,ArrayList就有利于节约内存空间。
如果涉及到堆栈,队列等操作,应该考虑用Vector,如果需要快速随机访问元素,应该使用ArrayList 。

Hashtable & HashMap 

Hashtable和HashMap它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。

 ArrayList & LinkedList

ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别:
从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能; 而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

来源:

Java 泛型

泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

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

定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

有界的类型参数:

可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

泛型类

  • 1、泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分
  • 2、和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
    实例:
public class Box<T> {
   
  private T t;
 
  public void add(T t) {
    this.t = t;
  }
 
  public T get() {
    return t;
  }
 
  public static void main(String[] args) {
    Box<Integer> integerBox = new Box<Integer>();
    Box<String> stringBox = new Box<String>();
 
    integerBox.add(new Integer(10));
    stringBox.add(new String("菜鸟教程"));
 
    System.out.printf("整型值为 :%d\n\n", integerBox.get());
    System.out.printf("字符串为 :%s\n", stringBox.get());
  }
}

输出结果:

整型值为 :10

字符串为 :菜鸟教程

类型通配符

  • 1、类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。
  • 2、类型通配符上限通过形如List<? extends Number> 来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

实例:

public class GenericTest {
     
    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        
        name.add("icon");
        age.add(18);
        number.add(314);
 
        //getUperNumber(name);//1
        getUperNumber(age);//2
        getUperNumber(number);//3
       
   }
 
   public static void getData(List<?> data) {
      System.out.println("data :" + data.get(0));
   }
   
   public static void getUperNumber(List<? extends Number> data) {
          System.out.println("data :" + data.get(0));
       }
}

输出结果:

data :18
data :314

解析: 在(//1)处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错

  • 3、类型通配符下限通过形如 List<? super Number> 来定义,表示类型只能接受Number及其三层父类类型,如Objec类型的实例。

<? extends T>和<? super T>的区别

  • 1、<? extends T>表示该通配符所代表的类型是T类型的子类。
  • 2、<? super T>表示该通配符所代表的类型是T类型的父类。

额外资料
泛型继承的几种写法

Java序列化

---更新到(一)

Java网络编程

java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

java.net 包中提供了两种常见的网络协议的支持

  • TCP:TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP
  • UDP:UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。

Socket 编程

这是使用最广泛的网络概念。
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行进行通信。

java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

使用套接字建立TCP连接的步骤:

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信
  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
  • 服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流

TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送。以下是一些类提供的一套完整的有用的方法来实现 socket。

服务器端

服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。

创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。

客户端

java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。

当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。

InetAddress 类的方法

这个类表示互联网协议(IP)地址。

客户端实例:

public class GreetingClient {

    public static void main(String[] args){
       String serverName = args[0];
       int port = Integer.parseInt(args[1]);
        System.out.println("连接到主机:" + serverName + " ,端口号:" + port);
        try {
            //创建一个流套接字并将其连接到指定主机上的指定端口号。
            Socket client = new Socket(serverName,port);
            //getRemoteSocketAddress()返回此套接字连接的端点的地址,如果未连接则返回 null。
            System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
            //返回此套接字的输出流。
            OutputStream outToServer = client.getOutputStream();
            //数据输出流(DataOutputStream)允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流。
            DataOutputStream out = new DataOutputStream(outToServer);
            //getLocalSocketAddress获取本地的IP和端口---输出流-传递信息给服务器端
            out.writeUTF(" Hello from "+client.getLocalSocketAddress());

            //输入流-获取服务器端返回的响应。
            InputStream inFormServer = client.getInputStream();
            DataInputStream in = new DataInputStream(inFormServer);
            System.out.println("服务器响应:"+in.readUTF());
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

服务端实例:

public class GreetingServer extends Thread {
    private ServerSocket serverSocket;

    public GreetingServer(int port) throws IOException{
        //创建绑定到特定端口的服务器套接字。
        serverSocket = new ServerSocket(port);
        //通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
        serverSocket.setSoTimeout(10000);
    }

    public void run(){
        while (true){

            try {
                System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
                //侦听并接受到此套接字的连接。
                Socket server = serverSocket.accept();
                System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
                //输入流-获取客户端传来的消息
                DataInputStream in = new DataInputStream(server.getInputStream());
                System.out.println("服务端打印:"+in.readUTF());
                //输出流-将服务端响应返给客户端
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "\nGoodbye!");
                server.close();
            } catch (SocketTimeoutException e){
                System.out.println("Socket timed out!");
                break;
            }catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    public static void main(String[] args){
        int port = Integer.parseInt(args[0]);
        try {
            Thread r= new GreetingServer(port);
            r.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

编译以上两个 java 文件代码,并执行以下命令来启动服务,使用端口号为 6066:

$ javac GreetingServer.java 
$ java GreetingServer 6066
等待远程连接,端口号为:6066...

新开一个命令窗口,执行以上命令来开启客户端:

$ javac GreetingClient.java 
$ java GreetingClient localhost 6066
连接到主机:localhost ,端口号:6606
远程主机地址:localhost/127.0.0.1:6606
服务器响应:谢谢连接我:/127.0.0.1:6606
Goodbye!

同时,服务端窗口会增加:

远程主机地址:/127.0.0.1:54277
服务端打印: Hello from /127.0.0.1:54277

若超过10s后,服务端会自动断开,并输出:

Socket timed out!

此时再通过客户端访问,会因为无服务连接报java.net.ConnectException: Connection refused: connect的错误。

URL 处理

URL(Uniform Resource Locator)中文名为统一资源定位符,有时也被俗称为网页地址。表示为互联网上的资源,如网页或者FTP地址。

URL可以分为如下几个部分。

protocol://host:port/path?query#fragment

protocol(协议) 可以是 HTTP、HTTPS、FTP 和 File,port 为端口号,path为文件路径及文件名。
HTTP 协议的 URL 实例如下:

https://windcoder.com/index.html?language=cn#j2se

URL 解析

  • 协议为(protocol):http
  • 主机为(host:port)windcodder.com
  • 端口号为(port): 80 ,以上URL实例并未指定端口,因为 HTTP - - 协议默认的端口号为 80。
  • 文件路径为(path):/index.html
  • 请求参数(query):language=cn
  • 定位位置(fragment):j2se,定位到网页中 id 属性为 j2se 的 HTML 元素位置 。

获取URL的各个部分参数:

实例:

public class URLDemo {
    public static void main(String[] args) {
        try {
            URL url = new URL("https://windcoder.com/index.html?language=cn#j2se");
            System.out.println("URL 为:" + url.toString());
            System.out.println("协议为:" + url.getProtocol());
            System.out.println("验证信息:" + url.getAuthority());
            System.out.println("文件名及请求参数:" + url.getFile());
            System.out.println("主机名:" + url.getHost());
            System.out.println("路径:" + url.getPath());
            System.out.println("端口:" + url.getPort());
            System.out.println("默认端口:" + url.getDefaultPort());
            System.out.println("请求参数:" + url.getQuery());
            System.out.println("定位位置:" + url.getRef());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

    }
}

由于未调用openConnection(),所以并未建立连接。

URLConnections 类方法

openConnection() 返回一个 java.net.URLConnection。
例如:

  • 如果你连接HTTP协议的URL, openConnection() 方法返回 HttpURLConnection 对象。
  • 如果你连接的URL为一个 JAR 文件, openConnection() 方法将返回 JarURLConnection 对象。
    等等...
    实例:
public class URLConnDemo {
    public static void main(String[] args) {
        try {
            //通过给定的URL字符串创建URL
            URL url = new URL("http://www.baidu.com");
            //打开一个URL连接,并运行客户端访问资源。连接HTTP协议的URL, openConnection() 方法返回 HttpURLConnection 对象。
            URLConnection urlConnection = url.openConnection();
            HttpURLConnection connection = null;
            if (urlConnection instanceof HttpURLConnection){
                connection = (HttpURLConnection) urlConnection;
            }else {
                System.out.println("请输入 URL 地址");
                return;
            }
            //读取资源
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuffer urlString = new StringBuffer();
            String current;
            while ((current =in.readLine())!=null){
                urlString.append(current);
            }
            System.out.println(urlString.toString());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java多线程

  • Java 给多线程编程提供了内置的支持。
  • 一个多线程程序包含两个或多个能并发运行的部分
  • 程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。
  • 多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销

进程

  • 一个进程包括由操作系统分配的内存空间,包含一个或多个线程
  • 一个线程不能独立的存在,它必须是进程的一部分。
  • 一个进程一直运行,直到所有的非守候线程都结束运行后才能结束。

生命周期

一个线程完整的生命周期

1、新建状态:

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

2、 就绪状态:

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度

3、运行状态:

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态

4、阻塞状态:

如果一个线程执行了sleep(睡眠)suspend(挂起) 等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

  • 1、等待阻塞运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
  • 2、同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
  • 3、其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

5、死亡状态:

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程的优先级

1、每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

2、Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

3、默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

4、具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

5、使用setPriority设置:

	Thread t1 = new MyThread1(); 
	Thread t2 = new Thread(new MyRunnable()); 
	t1.setPriority(10); 
	t2.setPriority(1); 

	t2.start(); 
	t1.start(); 

创建一个线程

Java 提供了三种创建线程的方法:

  • 1、通过实现 Runnable 接口;
  • 2、通过继承 Thread 类本身;
  • 3、通过 Callable 和 Future 创建线程。

创建线程的三种方式的对比

  • 1、采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类
  • 2、使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
  • 3、Runnable和Thread在执行完任务之后无法获取执行结果,但 Callable 和 Future可以。

通过实现 Runnable 接口来创建线程

创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。

为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

public void run()

该方法可以被重写, run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。

Thread 定义了几个构造方法,下面的这个是我们经常使用的:

Thread(Runnable threadOb,String threadName);

这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。
新线程创建之后,你调用它的 start() 方法它才会运行。

void start();

通过继承Thread来创建线程

创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。

继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行

该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

通过 Callable 和 Future 创建线程

  • 1、 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值
  • 2、 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
  • 3、 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  • 4、 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

接触Callable和Future太少,留些资料,之后深究。
Java程序员必须掌握的线程知识-Callable和Future
Java 多线程(七) 线程间的通信
JAVA中线程同步的方法(7种)汇总

其他

当一个线程进入一个对象的一个synchronized方法后,其它线程可以访问该对象的非同步方法。

一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个同步方法。

一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个对象的另一个同步方法。

当一个同步方法已经执行,线程能够调用对象上的非同步实例方法。

延伸
关于多线程常见问题
Java程序员面试中的多线程问题
当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
java 多线程面试题
从面试题看问题之线程篇(一)

Java Mysql连接

早期数据库操作

原理:一般来说,java应用程序访问数据库的过程是:

  • ①装载数据库驱动程序;
  • ②通过jdbc建立数据库连接;
  • ③访问数据库,执行sql语句;
  • ④断开数据库连接。

分析

  • 1、每一次web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。
    对于大型应用,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。
  • 2、对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。
  • 3、这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

技术演进出来的数据库连接池

数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。

大大提供了数据库连接的利用率,减小了内存吞吐的开销。

资料
谈谈数据库连接池的原理
Java连接池详解
谈谈数据库连接池的原理--转1

参与评论