前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 基础

Java 基础

原创
作者头像
云台大树
修改2021-09-13 14:50:52
4910
修改2021-09-13 14:50:52
举报
文章被收录于专栏:竟然限制用户只可建一个专栏

重点

java 泛型, 多线程, 集合, 网络编程, 流

Java 安装

设置JAVA_HOME

一是为了方便引用,比如,JDK安装在C:\jdk1.6.0目录里,则设置JAVA_HOME为该目录路径, 那么以后要使用这个路径的时候, 只需输入%JAVA_HOME%即可, 避免每次引用都输入很长的路径串;

二则是归一原则, 当JDK路径改变的时候, 仅需更改JAVA_HOME的变量值即可, 否则,就要更改任何用绝对路径引用JDK目录的文档, 要是万一没有改全, 某个程序找不到JDK, 后果是可想而知的----系统崩溃!

三则是第三方软件会引用约定好的 JAVA_HOME 变量, 不然, 你不能正常使用该软件。

在系统环境变量那一栏中点 -> 新建 JAVA_HOME (JAVA_HOME指向的是JDK的安装路径)

path 变量

path 变量使得我们能够在系统中的任何地方运行java应用程序,比如 javac、java、javah 等等,这就要找到我们安装 JDK 的目录,

假设我们的JDK安装在 C:\jdk1.6.0 目录下,那么在 C:\jdk1.6.0\bin 目录下就是我们常用的 java 应用程序,我们就需要把 C:\jdk1.6.0\bin 这个目录加到 path 环境变量里面。4

classpath 变量

classpath 环境变量,是当我们在开发java程序时需要引用别人写好的类时,要让 java 解释器知道到哪里去找这个类。通常,sun 为我们提供了一些额外的丰富的类包,一个是 dt.jar,一个是 tools.jar,这两个 jar 包都位于 C:\jdk1.6.0\lib 目录下,所以通常我们都会把这两个 jar 包加到我们的 classpath 环境变量中 set classpath=.;C:\jdk1.6.0\lib\tools.jar;C:\jdk1.6.0\lib\dt.jar

注意在完成配置环境变量后测试JDK是否安装成功时键入命令:java -version

Java 内部类

嵌在一个类中声明的类叫做内部类,有静态内部类和非静态内部类两种。 内部类可以访问外部类的属性和方法,这一特点非常好

非静态内部类 - 内部类有默认,privateprotected修饰符

静态内部类 - 静态内部类生成对象时,比非静态类方便些

代码语言:txt
复制
class OuterClass {
  public static void main(String[] args) {
    // 使用非静态内部类 - 先生成外部类对象,然后根据外部类对象生成内部类对象
    OuterClass myouter = new OuterClass();
    OuterClass.InnerClass myinner = myouter.new InnerClass();
    myinner.info();
    // 使用静态内部类 - 直接使用类名即可
    OuterClass.StaticInnerClass mySinner = new OuterClass.StaticInnerClass();
    System.out.println(mySinner.xx);
    OuterClass.StaticInnerClass.info();
  }
  private int y = 3;
  static private int m = 4;
  private void say() { System.out.println("hello"); }
  // 非静态内部类
  class InnerClass {
    public void info() {
      // 内部类可以访问外部类的属性-私有的也行
      System.out.println(y);
      say();
    }
  }
  // 静态内部类
  static class StaticInnerClass {
    int xx = 5;
    static public void info() {
      System.out.println(m);
    }
  }
}

Java 匿名类

在内部类基础上,如果类名没有给定,那么这就是一个匿名类。

匿名类主要用于在需要的时候创建一个对象执行特定任务,使得代码相比于内部类简洁.

匿名类通常继承一个父类或实现一个接口

代码语言:txt
复制
interface Polygon {
  void display();
}

class Father {
  public void display() {
    System.out.println("world");
  };
}

class OuterClass {
  // 匿名类 - 实现接口 - 实现了接口的方法
  public Polygon p1 = new Polygon(){
    public void display() {
      System.out.print("p1 hello");
    }
  };
  // 匿名类 - 继承父类 - 并重写了父类的方法
  public Father p2 = new Father() {
    public void display() {
      System.out.print("p2 hello");
    }
  };
  
  public static void main(String[] args) {
    OuterClass outer = new OuterClass();
    outer.p1.display();
    outer.p2.display();
  }
}

类型转换

基础类型自动转换

代码语言:txt
复制
低  ------------------------------------>  高
byte,short,char—> int —> long—> float —> double 

数据类型转换必须满足如下规则:

  • 不能对boolean类型进行类型转换。
  • 不能把对象类型转换成不相关类的对象。
  • 在把容量大的类型转换为容量小的类型时必须使用强制类型转换
  • 转换过程中可能导致溢出或损失精度,例如:
代码语言:txt
复制

int i =128;

byte b = (byte)i;

代码语言:txt
复制

因为 byte 类型是 8 位,最大值为127,所以当 int强制转换为 byte 类型时,值 128 时候就会导致溢出。

  • 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
代码语言:txt
复制

(int)23.7 == 23;

(int)-45.89f == -45

代码语言:txt
复制

修饰符

修饰符

当前类

同一包内

子孙类(同一包)

子孙类(不同包)

其他包

public

Y

Y

Y

Y

Y

protected

Y

Y

Y

Y/N(说明

N

default (默认)

Y

Y

Y

N

N

private

Y

N

N

N

N

修饰符的继承规则 - 一句话: 子类可以放大父类的权限,但是不能缩小!

  • 父类中声明为 public 的方法在子类中也必须为 public。
  • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
  • 父类中声明为 private 的方法,不能够被继承。

非访问修饰符

为了实现一些其他的功能,Java 也提供了许多非访问修饰符。

  • static 修饰符,用来修饰类方法和类变量。
  • final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
  • abstract 修饰符,用来创建抽象类和抽象方法。
  • synchronized 和 volatile 修饰符,主要用于线程的编程。
  • transient修饰符,用来预处理类和变量的数据类型。

Java StringBuffer和StringBuilder类

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 类在和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问),由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

Java数组

数组声明 dataType[] arrayRefVar

数组创建 arrayRefVar = new dataType[arraySize];

可以把两个合起来,如dataType[] arrayRefVar = new dataType[arraySize] 或字面量方式dataType[] arrayRefVar = {value0, value1, ...}

处理数组for循环或者for-each循环,后者无法在循环体内修改数组元素的值。 for-each的语法for( type element : array) {}

多维数组:真正的说,多维数组其实是一维数组,只是它的数组元素仍旧是数组元素 - 见下面的例子

dataType[][][] typeName = new dataType[length1][length2][length3]

length1表示第一维数组的数组长度(元素个数), length2为第一维数组元素指向的数组的数组长度, 依次类推!

代码语言:txt
复制
String[] s1Arr = {"hello", "world"};
String[] s2Arr = {"ok", "good"};
// sArr1是一个一维数组,它的数组元素类型是String[2](2个元素的String[])
String[][] sArr1 = { {"hello", "world"}, {"ok", "good"}};
String[][] sArr2 = {s1Arr, s2Arr};
System.out.println(sArr1[0][0]);
System.out.println(sArr2[0][0]);

Arrays类

java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。具有以下功能:

  • 给数组赋值:通过 fill 方法。
  • 对数组排序:通过 sort方法,按升序。
  • 比较数组:通过 equals方法比较数组中元素值是否相等。
  • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。

Java 日期时间

java.util包提供了 Date 类来封装当前的日期和时间。 Date 类提供两个构造函数来实例化Date 对象。

代码语言:txt
复制
Date now = new Date(); // 构造方法1
Date someDay = new Date(1600560136669L); // 构造方法2 - 注意输入的是long类型
System.out.println(now.getTime()); // 返回距离1970.01.01 00:00:00的毫秒数
System.out.println(someDay.toString()); // Sun Sep 20 08:02:16 CST 2020
// 返回String,格式为dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)

日期对象的比较 通过Date对象提供的3个方法可以比较日期对象的大小

  • 使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
  • 使用方法 before()after()equals()
  • 使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。

日期格式化输出 利用SimpleDateFormat类可以自定义日期时间输出,比Date类的输出更加友好。 还能利用printf格式输出

代码语言:txt
复制
Date now = new Date();
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd FF D E HH:mm:ss zzz"); // 入参为输出格式
System.out.println(ft.format(now)); // 2021-09-02 1 245 Thu 14:36:57 CST - F是从0开始的,0代表周日
代码语言:txt
复制
// 使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾 - c, F, D, r, T, R// c的使用 - Thu Sep 02 14:41:24 CST 2021System.out.printf("全部日期和时间信息:%tc%n", now);// f的使用 - 2021-09-02System.out.printf("年-月-日格式:%tF%n", now);// d的使用 - 09/02/21System.out.printf("月/日/年格式:%tD%n", now);// r的使用 - 02:41:24 PMSystem.out.printf("HH:MM:SS PM格式(12时制):%tr%n", now);// t的使用 - 14:41:24System.out.printf("HH:MM:SS格式(24时制):%tT%n", now);// R的使用 - 14:41System.out.printf("HH:MM格式(24时制):%tR", now);

SimpleDateFormat**格式表**

字母

描述

示例

G

纪元标记

AD

y

四位年份

2001

M

月份

July or 07

d

一个月的日期

10

h

A.M./P.M. (1~12)格式小时

12

H

一天中的小时 (0~23)

22

m

分钟数

30

s

秒数

55

S

毫秒数

234

E

星期几

Tuesday

D

一年中的日子

360

F

一个月中第几周的周几

2 (周日=0, 周一=1..)

w

一年中第几周

40

W

一个月中第几周

1

a

A.M./P.M. 标记

PM

k

一天中的小时(1~24)

24

K

A.M./P.M. (0~11)格式小时

10

z

时区

Eastern Standard Time

'

文字定界符

Delimiter

"

单引号

` | |

Java Calendar抽象类

可以看出使用Date类的使用有诸多不便, 比如无法获取日期时间数据中的一部分,如小时, 日,分钟。也不能对上述部分数据做计算,因此Calendar类就是为了解决这些问题。

代码语言:txt
复制
Calendar c = Calendar.getInstance();//默认是当前日期//创建一个代表2021年9月12日的Calendar对象Calendar c1 = Calendar.getInstance();c1.set(2021, 9, 12); c1.set(Calendar.DATE, 10); // 设置时间中的部分c1.add(Calendar.DATE, -10); // 对时间中的部分做计算// 获得月份int month = c1.get(Calendar.MONTH) + 1; // 月份从0开始!

GregorianCalendar类是Calendar类的一个具体实现!

Java 正则表达式

Java中正则表示通过包java.util.regex下的Pattern类和Matcher类实现。 前者表示正则表达式类,后者表示匹配类

匹配类中有三大种类方法: 索引方法, 查找方法, 替换方法

代码语言:txt
复制
    String content = "helloworld";    String patten = ".*llo.*";	// 快速用法 - 类型JS中正则表达式对象的test()方法 /.*llo.*/.test()    boolean isMatch = Pattern.matches(patten, content);	// 获取捕获结果 - 类似JS正则表示的exec()方法    String line = "This order was placed for QT3000! OK?";    String patten2 = "(\\D*)(\\d+)(.*)";    Pattern pattern = Pattern.compile(patten2);    Matcher match = pattern.matcher(line);    if (match.find()) {      // 第一个捕获组永远是全匹配的表达式      System.out.println("第一个捕获组" + match.group(0));      System.out.println("第一个捕获组" + match.group(2)); // 3000    }

该包下面还有一个异常类,用于捕获正则表达式的错误

Java 流(Stream) 文件(file)和IO

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。

Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。

控制台输入流

Java的控制台输入由System.in完成,可以把System.in包装在一个BufferReader对象里来创建一个字符流

该类的read()从输入流获取一个字符,并把该字符作为整数返回,当流结束时候返回-1。

该类的readLine()方法从输入流中获取一个字符串

代码语言:txt
复制
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));char c ;String str;try{    do{        c = (char)(bf.read());        System.out.print(c);     }while(c != 'Q');    do {        str = bf.readLine();        System.out.println(str);    }while(!str.equals("end"));}catch(IOException e) {    System.out.print(e.getMessage());}

文件流

一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。

下面是输入流和输出流的类层次图

<img src="https://www.runoob.com/wp-content/uploads/2013/12/iostream2xx.png" alt="img" style="zoom:80%;" />

代码语言:txt
复制
// 读取,存取文件主要是两个类FileInputStream 和 FileOutputStreamString path = "C:\\Users\\I334870\\Desktop\\hello.txt";byte bWrite[] = {11, 21, 3, 40, 5};try{    OutputStream ops = new FileOutputStream(path);    for(byte b : bWrite) {        ops.write(b);    }    ops.close();    InputStream ips = new FileInputStream(path);    int size = ips.available();    for(int i=0; i<size; i++) {        System.out.print((char)ips.read() + "|");    }    ips.close();}catch(IOException e) {    System.out.println(e.getMessage());}

上面用的是字节流输出和输入,可能导致控制台的输出是乱码,现在以字符流输出,以避免乱码

代码语言:txt
复制
String path = "C:\\Users\\I334870\\Desktop\\hello.txt";try{    // 写入!    File file = new File(path);    FileOutputStream fops = new FileOutputStream(file);    OutputStreamWriter writer = new OutputStreamWriter(fops, "UTF-8");    writer.append("hello"); // 写入缓冲区    writer.append("\n");    writer.append("world");    writer.close();    fops.close();    // 读取    FileInputStream fip = new FileInputStream(file);    InputStreamReader reader = new InputStreamReader(fip, "UTF-8");    StringBuffer sb = new StringBuffer();    while(reader.ready()) {        sb.append((char)reader.read());    }    System.out.println(sb.toString());    reader.close();    fip.close();}catch(IOException e) {    System.out.println(e.getMessage());}

文件目录

一些关于文件和I/O的类

  • File

该类由两个方法创建文件夹: mkdir()mkdirs()。第二个方法会根据路径依次创建文件夹及其父文件夹

  • FileReader
  • FileWriter

Java Scanner类

除了上述的BufferedReader类, 还有java.util.Scanner类获取输入流

代码语言:txt
复制
Scanner scanner = new Scanner(System.in);System.out.println("start input");while (scanner.hasNext()) {    String str1 = scanner.next();    System.out.print(str1);}while(scanner.hasNextLine() ) {    String str1 = scanner.nextLine();    System.out.print(str1);}scanner.close();

next() 与 nextLine() 区别

next():

  • 一定要读取到有效字符后才可以结束输入。
  • 对输入有效字符之前遇到的空白,next()方法会自动将其去掉。
  • 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
  • next()不能得到带有空格的字符串。

nextLine()

  • 以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
  • 可以获得空白。

如果要输入 intfloat类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用hasNextXxx()方法进行验证,再使用 nextXxx()来读取

Java 枚举

Java 枚举是一个特殊的类,一般表示一组常量(常量就是枚举类的实例!),使用 enum 关键字来定义,各个常量使用逗号 , 来分割。

代码语言:txt
复制
class OuterClass {  public static void main(String[] args) {    Color red = Color.RED;    for (Color col : Color.values()) {      switch (col) {        case RED:          System.out.println(Color.RED); break;        case BLUE:          System.out.println(Color.BLUE); break;        case GREEN:          System.out.println(Color.GREEN); break;      }    }  }  // 枚举类也可以作为内部类存在  enum Color {    RED, GREEN, BLUE;  }}

Java 包

如果一个类的包名如下,那么这个类路径是"p1/p2/p3/myclass.java"

代码语言:txt
复制
package p1.p2.p3;class myclass {}

编译时候,在java文件的目录下,不需要指定包名,但要指定class文件的输出地址javac -d . myclass.java

生成的字节码class文件会被放到对应的路径下面"p1/p2/p3/"

执行时候,在Java文件的目录下,要指定包名, java p1.p2.p3.myclass

为了在执行java命令时候方便,可以将包路径写入到CLASSPATH

Java 集合框架

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

集合框架一览图

<img src="https://www.runoob.com/wp-content/uploads/2014/01/2243690-9cd9c896e0d512ed.gif" alt="img" style="zoom:80%;" />

集合框架分层图

<img src="https://www.runoob.com/wp-content/uploads/2014/01/java-coll-2020-11-16.png" alt="img" style="zoom: 80%;" />

Java ArrayList类

ArrayList 类是一个可以动态修改的数组,提供了相关的添加、删除、修改、遍历等功能。

它支持按照索引来访问元素!

<img src="https://www.runoob.com/wp-content/uploads/2020/06/ArrayList-1-768x406-1.png" alt="img" style="zoom:80%;" />

使用该集合类的语法是 该类提供另外的方法,具体可以见帮助文档

代码语言:txt
复制
import java.util.ArrayList;ArrayList<E> objectName = new ArrayList<>();ArrayList<Integer> cities = new ArrayList<>();// 添加元素cities.add(1);cities.add(2);cities.add(3);cities.add(5);// 修改元素cities.set(2, 3);// 移除元素cities.remove(3);// 访问元素System.out.print(cities.get(2));// 排序Collections.sort(cities);

Java LinkedList类

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

它不能按照索引来访问元素!与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低。

LinkedList是一个多面手啊, 实现了真他妈多的接口!

LinkedList 继承了 AbstractSequentialList类。

LinkedList 实现了 Queue 接口,可作为队列使用。

LinkedList 实现了List接口,可进行列表的相关操作。

LinkedList实现了 Deque 接口,可作为队列使用。

LinkedList实现了Cloneable 接口,可实现克隆。

LinkedList实现了java.io.Serializable接口,即可支持序列化,能通过序列化去传输。

img
img

Java HashMap类

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步,是无序的,即不会记录插入的顺序

img
img

创建HashMap的语法及常用方法是

代码语言:txt
复制
HashMap<String, String> sites = new HashMap<String, String>();// 添加map元素sites.put("1", "google");sites.put("2", "Taobao");// 获取元素sites.get("2");// 移除元素 sites.remove("2");sites.clear(); // 全部移除for(Entry<String, String> entry : sites.entrySet()) { /* */ }for(String key : sites.keySet()) { /* */ }for(String value: sites.values()) { /* */ }

Java HashSet类

java.util.HashSet是基于HashMap类来实现的,是一个不允许有重复元素的集合, HashSet是无序的,它不是线程安全

<img src="https://www.runoob.com/wp-content/uploads/2020/07/java-hashset-hierarchy.png" alt="img" style="zoom:80%;" />

Java Iterator 迭代器

迭代器Java Iterator不是一个集合,是一种访问集合的方法,可用于迭代ArrayListHashSet等集合。

迭代器的基本操作是next(), hasNext()remove()

java.util.Iterator类是最简单的迭代器实现

代码语言:txt
复制
ArrayList<String> cities = new ArrayList<String>();cities.add("ningbo");cities.add("shanghai");cities.add("beijing");Iterator<String> it = cities.iterator();while(it.hasNext()) {    String city = it.next();    if(city.equals("beijing")) {        it.remove();        continue;    }    System.out.println(city);}

Java 泛型

根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

普通类中的泛型方法

所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前

代码语言:txt
复制
public static void main(String[] args) {    Integer[] intArray = {1, 2, 3, 4, 5};    Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};    String[] strArray = {"1", "2", "3", "4"};    printArray(intArray);    printArray(doubleArray);    printArray(strArray);}// 泛型方法声明! 泛型 <E> 放在返回值之前public static <E> void printArray(E[] inputArray) {    for( E element : inputArray) {        System.out.printf("%s ", element);    }    System.out.println();}

泛型方法中有界的类型参数: 用于限制允许船吊一个类型参数的类型种类范围,如一个泛型只希望接收Number或者Number的子类

在泛型类型后,使用关键字extends,后续紧跟它的上界 就像类继承中的extends表示泛型是扩展自某个基类

代码语言:txt
复制
class MaximumClass {  public static void main(String[] args) {    Integer intMax = maximum(3, 4, 5);    Double doubleMax = maximum(3.3, 4.4, 5.5);    String strMax = maximum("hello", "hellO", "helloa");    System.out.println(intMax);    System.out.println(doubleMax);    System.out.println(strMax);  }  // 类型T必须继承自实现了Comparable接口的类  public static <T extends Comparable<T> > T maximum(T x, T y, T z) {    T max = x;     if ( y.compareTo(max) > 0 ) {      max = y;    }    if ( z.compareTo(max) > 0 ) {      max = z;    }    return max;  }}

泛型类

泛型类 : 泛型类的声明和非泛型类的声明类似,泛型类的类型参数声明部分包含一个或者多个类型参数,必须指定!

注意!在泛型类的声明中必须给定泛型参数!

代码语言:txt
复制
class OuterClass<T>  {  private T t;  public void add(T t) {    this.t = t;  }  public T get() {    return t;  }  public static void main(String[] args) {    OuterClass<Integer> intOuterClass = new OuterClass<>();    OuterClass<String> strOuterClass = new OuterClass<>();    intOuterClass.add(3);    strOuterClass.add("hello");    System.out.printf("整数值:%d \n", intOuterClass.get());    System.out.printf("字符串:%s \n", strOuterClass.get());  }}

类型通配符

类型通配符使用?代替具体的参数,如List<?>表示List<String> List<Integer>等所有List<具体类型实参>的父类

注意是List<具体类型实参>的父类,不是具体类型实参的父类!

类型通配符可以结合extends来限定具体类型参数的上限, 可以结合super来限定具体类型参数的下限

代码语言:txt
复制
import java.util.*;class OuterClass {  // 参数中使用了类型通配符  public static void getData(List<?> data) {    System.out.println("data :" + data.get(0));  }  // 类型通配符可以结合extends来限定具体参数类型的上限 - 只接受该类型及其子类  public static void getUperNumber(List<? extends Number> data) {    System.out.println("data: " + data.get(0));  }  // 类型通配符结合super来限定具体参数类型的下限 - 只接受该类型及其父类  public static void getLowerNumber(List<? super Number> data) {    System.out.println("data:" + data.get(0));  }  public static void main(String[] args) {    List<String> name = new ArrayList<>();    List<Integer> age = new ArrayList<>();    List<Number> number = new ArrayList<>();    name.add("hello");    age.add(3);    number.add(3.3);    getData(name);    getData(age);    getData(number);    // getUperNumber(name); // 编译错误!    getUperNumber(age);     // getLowerNumber(age); // 编译错误!    getLowerNumber(number);  }}

Java 序列化

对象序列化提供一种对对象序列化的机制,该机制下,一个对象可以表示为一个字节序列,序列中包含该对象的数据、对象的类型信息和存储在对象中的数据类型。被序列化的对象也可以被反序列化。

序列化及反序列化过程独立于Java虚拟机,这说明一个平台上序列化的对象可以在另一个不同的平台上被反序列化。

序列化成功的条件

  • 该类必须实现java.io.Serializable接口
  • 该类的所有属性必须是可序列化的。如果有不可序列化属性,该属性必须被注明是短暂的,即transient修饰符。

注意: 当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。 借助FileOutputStreamObjectOutputStream两个类用于序列化输出; 借助FileInputStreamObjectInputStream两个类用于逆序列化

代码语言:txt
复制
import java.util.*;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;import java.io.Serializable;class OuterClass implements java.io.Serializable{  public static void main(String[] args) {    String filePath = "c:/Users/I334870/Desktop/employee.ser";    OuterClass outclass = new OuterClass();    try {      Employee employee = outclass.new Employee("Hello", "Shanghai", 1334870, 101);      // 借助FileOutputStream 和 ObjectOutputStream 两个类      FileOutputStream fileOut = new FileOutputStream(filePath);      ObjectOutputStream out = new ObjectOutputStream(fileOut);      out.writeObject(employee);      out.close();      fileOut.close();      System.out.println("Serialized data is saved in your desktop");    } catch (IOException e) {      e.printStackTrace();    }    DeserializeDemo demo = outclass.new DeserializeDemo();    demo.deserializeEmployee(filePath);  }      class DeserializeDemo {    public void deserializeEmployee(String filePath) {      Employee employee = null;      try{        // 借助FileInputStream 和 ObjectInputStream 两个类        FileInputStream fileIn = new FileInputStream(filePath);        ObjectInputStream in = new ObjectInputStream(fileIn);        employee = (Employee) in.readObject();        in.close();        fileIn.close();      } catch(IOException e) {        e.printStackTrace();        return;      } catch(ClassNotFoundException c) {        System.out.println("Class Employee not found");        c.printStackTrace();        return;      }      System.out.println("Name = " + employee.name);      System.out.println("Address = " + employee.address);      System.out.println("SSN = " + employee.SSN);      System.out.println("Number = " + employee.number);;    }  }      class Employee implements java.io.Serializable {    public String name;    public String address;    // 当对象被序列化时,属性SSN的值为1334870,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后Employee对象的SSN属性为0    public transient int SSN;    public int number;      public Employee(String name, String address, int SSN, int number) {      this.name = name;      this.address = name;      this.SSN = SSN;      this.number = number;    }    public Employee(){}      public void mailCheck() {      System.out.println("Mailling a check to " + name + " " + address);    }  }}

Java 发送邮件

使用Java应用程序发送电子邮件,需要在机器上安装JavaMail API和Java Activation Framework(JAF)

下载后,需要把对应的mail.jar和activation.jar文件添加到CLASSPATH

代码语言:txt
复制
import java.util.*;import javax.mail.*;import javax.mail.internet.*;import javax.activation.*;class OuterClass {  public static void main(String[] args) {    String sendTo = "hello@gmail.com";    String sendFrom = "me@gmail.com";    String host = "localhost";    // 获取系统属性    Properties properties = System.getProperties();    properties.setProperty("mail.smtp.host", host);        Session session = Session.getDefaultInstance(properties);    try {      // 创建默认的MIME message对象      MimeMessage message = new MimeMessage(session);      message.setFrom(new InternetAddress(from));      // 还可以在此设置CC, 或者BCC      message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));      message.setSubject("this is subject");      message.setContent("<h1>this is body msg</h1>", "text/html");      // 发送消息      Transport.send(message;    } catch (MessageingException mex) {      mex.printStackTrace();    }  }}

Java网络编程

java.net包中的类和接口,提供了低层次的通信细节。该包支持TCPUDP这两种常见的协议。

Socket编程 - TCP / Socket编程由java.net.socket类完成。以下是两台计算机之间建立TCP连接过程的步骤

  • 服务器实例化一个ServerSocket对象,表示通过服务器上的端口通信;
  • 服务器调用ServerSocket类的accept()方法,该方法将一直等待,直至客户端连接到服务器给定的端口;
  • 服务器正在等待时,客户端实例化一个Socket对象,指定服务器名和端口号来请求连接;
  • Socket类的构造函数将客户端连接到指定的服务器和端口,以建立通信;
  • 在服务端,accept()方法返回服务器上的一个新的Socket对象,该Socket对象连接到客户端的Socket对象

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

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

下面的代码演示阻塞式IO的网络编程(BIO),但是Java中还有NIO*(同步非阻塞式IO编程), AIO(异步非阻塞式IO编程)这点上类似Node.js

java.nio.channels包下增加了四个异步通道:AsynchronousSocketChannel, AsynchronousServerSocketChannel, AsynchronousFileChannel, AsynchronousDatagramChannel

代码语言:txt
复制
// client socketimport java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;public class Client {  public static void main(String[] args) {    String serverName = args[0];    int port = Integer.parseInt(args[1]);    try {      System.out.println("Connecting to server " + serverName + ", port " + port);      // create client socket, connect to server      Socket client = new Socket(serverName, port);       System.out.println("Remote server address:" + client.getRemoteSocketAddress());      // send messsage to server      OutputStream outToServer = client.getOutputStream();      DataOutputStream out = new DataOutputStream(outToServer);      out.writeUTF("hello, from " + client.getLocalSocketAddress());      // receive message from server      InputStream inFromServer = client.getInputStream();      DataInputStream in = new DataInputStream(inFromServer);      System.out.println("Server respondes:" + in.readUTF());      client.close();    } catch (IOException io) {      io.printStackTrace();    }  }}
代码语言:txt
复制
// Server Socketimport java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketTimeoutException;public class Server extends Thread {  private ServerSocket serverSocket;  public Server(int port) throws IOException {    serverSocket = new ServerSocket(port);    serverSocket.setSoTimeout(100000);  }  public void run() {    while(true) {      try {        System.out.println("Waiting for clients on port:" + serverSocket.getLocalPort() + "...");        // create server socket - it waits until a client socket connects, but the maximum waiting time can be set        Socket server = serverSocket.accept();        System.out.println("Remote address: " + server.getRemoteSocketAddress());        // receive message from client        DataInputStream in = new DataInputStream(server.getInputStream());        System.out.println(in.readUTF());        // send message to client        DataOutputStream out = new DataOutputStream(server.getOutputStream());        out.writeUTF("Welcome: " + server.getLocalSocketAddress() + "\n goodbye!");        server.close();      } catch (SocketTimeoutException timeout) {        System.out.println("Socket timed out");        break;      } catch (IOException io) {        io.printStackTrace();        break;      }    }  }  public static void main(String[] args) {    int port = Integer.parseInt(args[0]);    try {      Thread thread = new Server(port);      thread.run();    } catch (IOException io) {      io.printStackTrace();    }  }}

Java 多线程编程

一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

进程 vs 线程

进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。

线程:不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束

线程的状态

  • 新建状态

使用newThread类或其子类建立一个线程,该线程对象就处于新建状态;

  • 就绪状态

线程对象调用start()方法后,该线程对象进入就绪状态,等待JVM里线程调度器的调度

  • 运行状态

就绪状态的线程获取CPU资源,就可以执行run()方法,该线程处于运行状态。该状态可以变为就绪、阻塞、死亡;

  • 阻塞状态

如果线程对象执行了睡眠sleep(), 挂起suspend()等方法,失去所占资源后,该线程进入阻塞状态;时机成熟后,再次进入就绪状态;

  • 死亡状态

线程完成任务或其他终止条件出发,该线程就进入死亡状态。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY) , 5 (NORM_PRIORITY)^默认^, 10(Thread.MAX_PRIORITY)

优先级的设置不能完全保证线程按照优先级被调度,具体由平台决定

线程的重要概念

  • 线程同步
  • 线程间通信
  • 线程安全
  • 线程死锁
  • 线程控制:挂起、停止和恢复

创建线程的3种方式

  • 通过实现Runnable接口
  • 通过继承Thread
  • 通过CallableFuture创建线程

采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread()方法,直接使用 this 即可获得当前线程。

通过Runnable接口创建线程

最简单的方式创建一个线程,就是实现Runnable接口,然后重写该接口的run()

代码语言:txt
复制
import java.lang.Runnable;public class RunnableDemo implements Runnable {  private Thread t;  private String threadName;    RunnableDemo(String name) {    threadName = name;    System.out.println("Creating " + threadName);  }  @Override  public void run() {    // JVM线程调度器调用线程的run(), 进入运行状态    System.out.println("Running " + threadName);    try {      for(int i = 4; i > 0; i--) {        System.out.println("Thread: " + threadName + ", " + i);        Thread.sleep(300);      }    } catch ( InterruptedException e) {      System.out.println("Thread " + threadName + "interrupted.");    }    System.out.println("Thread " + threadName + " exiting.");  }  public void start() {    System.out.println("Starting " +  threadName);    if (t == null) {      // 新建状态      t = new Thread(this, threadName);      // 就绪状态      t.start();    }  }}

通过继承Thread来创建线程

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

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

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

从代码上看,继承方式同实现方式创建线程的代码和过程是类似的!

代码语言:txt
复制
public class ThreadDemo {  private Thread t;  private String threadName;  public ThreadDemo(String name) {    threadName = name;    System.out.println("Thread " + threadName + " created");  }  public void run() {    System.out.println("Running " + threadName);    try {      for (int i = 4; i > 0; i--) {        System.out.println("Thread: " + threadName + ", " + i);        Thread.sleep(300);      }    } catch (InterruptedException e) {      System.out.println("Thread " + threadName + "interrupted.");    }    System.out.println("Thread " + threadName + " exiting.");  }  public void start() {    System.out.println("Starting " + threadName);    if (t == null) {      t = new Thread(this, threadName);      t.start();    }  }}

通过CallableFuture创建线程

  • 创建Callable接口的实现类,实现call()方法,该方法是线程的执行体,并且有返回值
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FuturnTask对象封装了该Callable对象的call()方法返回值
  • 使用FuturnTask对象作为Thread对象的target创建并启动新线程
  • 调用FuturnTask对象的get()方法来获取子线程执行后的返回值

相比于其他两种方式,这一个方式能够获取线程的返回值

代码语言:txt
复制
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class CallableThreadTest implements Callable<Integer> {  public static void main(String[] args) {    CallableThreadTest ctt = new CallableThreadTest();    FutureTask<Integer> ft = new FutureTask<>(ctt);    for( int i = 0; i < 100; i++) {      System.out.println(Thread.currentThread().getName() + " 循环变量i的值" + i);      if ( i == 20 ) {        new Thread(ft, "有返回值的线程").start();      }    }    try {      System.out.println("子线程的返回值:" + ft.get());    } catch (InterruptedException e) {      e.printStackTrace();    } catch(ExecutionException e) {      e.printStackTrace();    }  }  @Override  public Integer call() throws Exception {    int i=0;    for(; i<100; i++) {      System.out.println(Thread.currentThread().getName() + " " + i);    }    return i;  }}

Java MySQL连接

Java 连接 MySQL 需要驱动包,从官方下载后,解压后得到 jar 库文件,然后在对应的项目中导入该库文件。

代码语言:txt
复制
import java.sql.Statement;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;public class MySQLDemo {  static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  static final String DB_URL = "jdbc:mysql:<host>:<port>/<db>";  static final String USER = "root";  static final String PASS = "123456";  public static void main(String[] args) {    Connection conn = null;    Statement stmt = null;    try {      // 注册JDBC驱动      Class.forName(JDBC_DRIVER);      // 打开链接      System.out.println("连接数据库...");      conn = DriverManager.getConnection(DB_URL, USER, PASS);      // 执行查询      System.out.println("实例化SQL命令对象...");      stmt = conn.createStatement();      String sql = "SELECT id, name, url FROM websites";      ResultSet rs = stmt.executeQuery(sql);      // 展开结果集数据      while( rs.next() ) {        int id = rs.getInt("id");        String name = rs.getString("name");        String url = rs.getString("url");        /* do something  else*/      }       rs.close();      stmt.close();      conn.close();    } catch (SQLException se) {      se.printStackTrace();    } catch (Exception e) {      e.printStackTrace();    } finally {      try {        if (stmt != null) stmt.close();      } catch(Exception e) { /* do nothing */}      try {        if (conn != null) conn.close();      } catch (Exception e) { /* do nothing */}    }    System.out.println("ends");  }}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 重点
  • Java 安装
  • Java 内部类
  • Java 匿名类
  • 类型转换
  • 修饰符
  • Java StringBuffer和StringBuilder类
  • Java数组
    • Arrays类
    • Java 日期时间
    • Java Calendar抽象类
    • Java 正则表达式
    • Java 流(Stream) 文件(file)和IO
    • Java Scanner类
    • Java 枚举
    • Java 包
    • Java 集合框架
    • Java ArrayList类
    • Java LinkedList类
    • Java HashMap类
    • Java HashSet类
    • Java Iterator 迭代器
    • Java 泛型
      • 普通类中的泛型方法
        • 泛型类
          • 类型通配符
          • Java 序列化
          • Java 发送邮件
          • Java网络编程
          • Java 多线程编程
            • 通过Runnable接口创建线程
              • 通过继承Thread来创建线程
                • 通过Callable和Future创建线程
                • Java MySQL连接
                相关产品与服务
                云数据库 MySQL
                腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档