0%

实例化InetAddress类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.sympa.lesson01;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {

public static void main(String[] args) {
try {
//获取本机IP
InetAddress inet1 = InetAddress.getLocalHost();
System.out.println(inet1);

//获取指定域名IP
InetAddress inet2 = InetAddress.getByName("baidu.com");
System.out.println(inet2.getHostAddress());

} catch(UnknownHostException e) {
}
}
}

  • 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)
  • 从JDK1.5以后,Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象
  • JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参
  • 泛型类型不能是基本数据类型
  • 静态方法不能使用类的泛型,因为静态方法早于类的创建
  • 异常类不能是泛型的
  • 不可以T[] arr = new T[],可以T[] arr = (T[]) new Object[],因为T不是实际的类

自定义泛型结构

泛型类,泛型接口,泛型方法

  • 如果定义了类是带泛型,建议在实例化时要指明泛型类型
  • 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
  • 泛型方法在返回值前加,否则会被误认为是确定的类

通过通配符定义泛型对象公共父类

1
2
3
4
5
List<Integer> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
  • 对于List<?>,不能向其内部添加数据,除了添加null
  • 可以使用Object类型读取
有限制条件的通配符
  • 通配符指定上限

    上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口

  • 通配符指定下限

    下限super:使用时指定的类型不能小于操作的类

  • 举例

    • <? extends Number> (无穷小, Number]

      只允许泛型为Number及Number子类的引用调用

    • <? super Number> [Number, 无穷大)

      只允许泛型为Number及Number父类的引用调用

    • <? extends Comparable>

      只允许泛型为实现Comparable接口的实现类引用调用

  • JDK的元Annotation用于修饰其他Annotation定义
  • JDK5.0提供了4个标准的meta-annotation类型,分别是:
    • Retention
    • Target
    • Documented
    • Inherited
  • @Retention:只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期,@Rentention包含一个RetentionPolicy类型的成员变量,使用@Rentention时必须为该value成员变量指定值:
    • RetentionPolicy.SOURCE:在源文件中有效
    • RetentionPolicy.CLASS:在class文件中有效(默认)
    • RetentionPolicy.RUNTIME:在运行时有效,能通过反射获取
  • @Target:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些元素,@Target也包含一个名为value的成员变量
取值 取值
CONSTRUCTOR 用于描述构造器 PACKAGE 用于描述包
FIELD 用于描述域 PARAMETER 用于描述参数
LOCAL_VARIABLE 用于描述局部变量 TYPE 用于描述类,接口或enum声明
METHOD 用于描述方法
  • @Documented:用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的
  • 定义为Documented的注解必须设置Retention值为RUNTIME
  • @Inherited:被它修饰的Annotation将具有继承性,子类将自动具有该注解

  • JDK5.0开始,Java增加了对元数据(MetaData)的支持,也就是注解
  • Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入补充信息。代码分析工具,开发工具和部署工具可以通过这些补充信息进行验证或部署
  • Annotation可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明
  • 在JavaSE中,注解使用目的较为简单,例如标记过时功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何界面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等
  • 未来的开发模式都是基于注解的,一定程度上:框架 = 注解 + 反射 + 设计模式。

常见Annotation

  • 生成文档相关注解
    • @author 标明开发该类模块的作者,多个作者直接使用,分隔
    • @version 标明该类模块的版本
    • @see 参考转向,也就是相关主题
    • @since 从哪个版本开始增加的
    • @param 对方法中某参数的说明,如果没有参数就不能写。格式:@param 形参名 形参类型 形参说明
    • @return 对方法返回值说明。格式:@return 返回值类型 返回值说明
    • @exception 对方法可能抛出异常说明,没有throws显式抛出异常就不能写。格式:@exception 异常类型 异常说明
  • 在编译时进行格式检查(JDK内置三个基本注解)
    • @Override:限定重写父类方法
    • @Deprecated:表示所修饰的元素已过时
    • @SuppressWarnings:抑制编译器警告
  • 跟踪代码依赖性,实现替代配置文件功能

如何定义枚举类

  • jdk5.0之前,自定义枚举类
  • 之后,可以使用enum关键字

自定义枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Season{
private final String seasonName;
private final String seasonDesc;
//1.私有化类的构造器
private Season(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}

//2.提供当前枚举类多个对象
public static final Season SPRING = new Season("春天", "春暖花开");
}

//3.其他诉求1:获取枚举类对象属性
//4。其他诉求2:提供toString()

enum关键字(默认继承于Enum类)

1
2
3
4
5
enum Season{
//多个之间用,分隔
SPRING("春天", "春暖花开");
//toString返回对象名
}

使用enum关键字定义枚举类实现接口

  • 情况一:在enum枚举类中实现抽象方法
  • 情况二:在每个对象后添加代码块实现方法

Java实现对象排序方式

  • 自然排序:java.lang.Comparable
  • 定制排序:java.util.Comparator

Comparable接口(自然排序)

  • 重写compareTo(obj)方法
  • 当前对象this大于形参对象obj,返回正整数,反之。
1
2
3
4
5
6
7
8
9
10
11
@Override
public int compareTo(Object o) {
if(o instanceof Goods){
Goods goods = (Goods)o;
if(this.price == goods.price){
return 0;
}
return this.price < goods.price;
}
throw new RuntimeException("数据类型不一致!");
}

Comparator接口(定制排序)

  • 重写compare(Object o1, Object o2)方法

  • String(JDK1.0):不可变字符序列
  • StringBuffer(JDK1.0):可变字符序列,效率低,线程安全
  • StringBuilder(JDK5.0):可变字符序列,效率高,线程不安全

可变字符序列

  • 创建时默认char[16](无参),可以指定容量(带参)
  • 扩容:2倍 + 2,还不够直接扩容增加的长度

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具
  • 好处:
    • 提高响应速度(减少创建线程时间)
    • 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
    • 便于线程管理
      • corePoolSize:核心池大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

  • JDK5.0起提供了线程池相关API:ExecutorServise和Executors
  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
    • Futuresubmit(Callabletask):执行任务,有返回值,一般用来执行Callable
    • void shutdown():关闭连接池
  • Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
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
package com.sympa.lesson01;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {

public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小

ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());

service.shutdown();
}

}

class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

Java提供了几个方法解决线程之间的通信问题

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

注意:均是Object类方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateExption

并发协作模型“生产者/消费者模式”

解决方式1:管程法

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”
  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

解决方式2:信号灯法

  • 设置标志位

  • 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
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
package com.sympa.lesson01;

import java.util.concurrent.locks.ReentrantLock;

public class Tickets01 {

public static void main(String[] args){
Tickets tickets = new Tickets();
new Thread(tickets, "黄牛1号").start();
new Thread(tickets, "黄牛2号").start();
new Thread(tickets, "黄牛3号").start();
}
}
class Tickets implements Runnable{

private int ticket = 10;
boolean flag = true;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(flag) {
buy();
}
}

public void buy() {
try{
lock.lock();
if(ticket == 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticket-- + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}

synchronized与lock的对比

  • Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:
    • Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体外)