复习
线程优先级:10个优先级,最好只有3个,最低1、普通的5、最高的10,主线程默认优先级是5,优先级有继承关系,优先级高的线程,创建的子线程优先级也高
守护线程:
- 为用户线程(User Thread)提供支持服务的线程
- 在Java中,一个线程要么是User Thread,要么是Daemon Thread
- 如果程序中有User Thread在运行,则Daemon Thread也会一直运行,如果程序中所有的User Thread都结束运行,则所有的Daemon Thread也停止运行
- 如何设置:
- t1.setDaemon(true);
- t1.isDaemon();
生产者和消费者:
- 生产者:创建或增加资源的线程
- 消费者:消耗资源的线程
- 生产者和消费者是并发的对同一个资源进行读写操作,会存在线程安全问题,需要进行控制
- 如何控制:
- 将增加资源的方法和减少资源的方法都设为同步方法,并且使用相同的锁,使同一时间仅有1个线程可以对资源进行修改操作
- 如果当前资源不够消费者来消费,消费者需要等到资源够了再进行消费。这里使用wait()方法使消费者进入阻塞状态,当生产者添加完资源后,会使用notify()或
notifyAll()
通知消费者醒来,验证当前资源是否足够消费,如果不够,再次进入阻塞状态。
网络编程
IP地址:一台主机在互联网中的地址,格式32位2进制,用3个点分割成4部分,每一部分是8个2进制数字,一般使用0-255的10进制数来表示
域名(Domain name):网址,以文字的形式来表示ip地址,建立文字表述与ip地址的映射,用户仅需要记忆域名,不需要记忆ip地址
DNS:一个维护了域名和ip地址的对应关系的分布式数据库,可以提供基于域名查询IP地址的服务
端口号:
- 当访问一台主机没有指定端口号时,默认访问的是80端口
TCP/IP:一组互联网协议
TCP:一个长连接协议,可靠,稳定,面向流
- Socket
- ServerSocket
UDP:一个无连接协议,不可靠,面向数据报
- DatagrameSocket
什么是Socket?所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
解决的问题:如果程序中要求某个类在整个程序运行期间,只能有唯一的一个实例
什么时候有这种要求:
- 需要使用唯一的一个对象来进行统一的管理,如果对象多了,则程序会出问题
- 例如:
- 线程池管理对象
- 数据库连接池管理对象
- 计数器管理对象
- 例如:
- 相对于频繁创建对象,使用唯一的一个对象有更高的效率
- 比如需要调用某个类的实例方法,但是与该类的属性没关系
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
怎么能保证一个类在程序运行过程中,只能有唯一的一个对象?
- 私有化类的构造器,禁止从类的外部去创建对象
- 在类的内部创建该类的私有的静态的唯一实例
- 在该类内部提供一个共有的,静态的,返回唯一实例的方法
介绍
**意图:**保证一个类仅有一个实例,并提供一个访问它的全局访问点。
**主要解决:**一个全局使用的类频繁地创建与销毁。
**何时使用:**当您想控制实例数目,节省系统资源的时候。
**如何解决:**判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
应用实例:
- 1、一个班级只有一个班主任。
- 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
- 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
**缺点:**没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
**注意事项:**getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
单例模式的几种实现方式
单例模式的实现有多种方式,如下所示:
1、饿汉式
**是否 Lazy 初始化(懒加载/延迟加载):**否
**是否多线程安全:**是
**实现难度:**易
**描述:**这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
2、懒汉式
**是否 Lazy 初始化:**是
**是否多线程安全:**否
**实现难度:**易
**描述:**这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
总结:
- 如果需要一个类在程序运行过程中仅能有唯一的一个实例,要使用单例模式
- 单例模式的要求:
- 私有的构造器
- 私有的静态的本类的唯一实例
- 公有的静态的返回本类唯一实例的方法
- 单例模式的实现方式:
- 饿汉式
- 不延迟创建本类的唯一实例,类加载时即创建
- 优点:
- 代码实现简单
- 是线程安全的
- 缺点:
- 占用资源
- 会延长类的加载时间
- 懒汉式
- 延迟创建本类的唯一实例,第一次尝试获取实例时创建
- 优点:
- 节省类的加载时间
- 减少不必要的资源占用
- 缺点:
- 线程不安全
- 为了实现线程安全,需要较为复杂的代码
- 饿汉式
反射
- 反射库(reflection library) 提供了一个非常丰富且精心设计的工具集,以便编写能动态操纵Java代码的程序。
- 对于任意一个类,都能够知道这个类的所有属性和方法
- 对于任意一个类,可以动态的创建它的对象
- 对于任意一个对象,都能够调用它的任意方法和属性
反射相关类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的属性 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
- 可以从面向对象的角度理解上述的类的设计
获取Class对象
Class对象并不是开发者创建的,Java在加载完一个.class文件后,会创建一个Class对象,代表已加载的这个.class文件,在Class对象中封装了该类的相关信息。
- 通过类型.class返回对应的字节码对象
- Cat.class 要求:在写代码时,该类已经在你的项目中,否则该编码编译时不通过
- 通过对象调用getClass方法返回对应的字节码对象
- 实例方法,需要通过对象去调用
- 通过给定字符串来(Class.forName)获取对应的字节码对象(保证字符串内容没有问题)
- 通过类的包名.类名从项目目录中查找并加载.class文件到内存中
- 因为包名.类名是以字符串的形式存在的,因此,写代码时,该类文件可能并不存在
public class ClassDemo1{
public static void main(String[] args) throws ClassNotFoundException{
//1.通过leix.class返回字节码对象
//类的字节码对象
//Class<String> clz=String.class;
//接口的字节码对象
//Class<List> clz=List.class;
//基本类型的字节码对象
/*Class clz=int.class;
System.out.println(clz);*/
//2.通过对象调用getClass方法返回字节码对象
/*Class<String> clz=(Class<String>)"abc".getClass();
System.out.println(clz);*/
//3.通过字符串来返回字节码对象(字符串内容内容没有问题)
Class<String> clz=(Class<String>)Class.forName("java.lang.String");
System.out.println(clz);
}
}
通过反射创建对象
- 字节码对象调用newInstance()执行无参构造
- 构造方法类的对象调用newInstance有参方法执行有参构造并且赋值
public class ClassDemo2{
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException{
//获取字节码对象
/*Class<String> clz=String.class;
//调用newInstance()执行无参构造返回实例对象
//String str=clz.newInstance();
//获取有参构造
Constructor c=clz.getConstructor(String.class);
//执行有参构造并且赋值
String str=(String)c.newInstance("def");
//String str1=new String("abc");
System.out.println(str);*/
//通过有参构造来创建Integer对象
//获取字节码对象
Class<Integer> clz=Integer.class;
//获取指定构造方法
Constructor c=clz.getConstructor(int.class);
//执行有参构造并且赋值
Integer in=(Integer)c.newInstance(123);
//
System.out.println(in);
}
}
总结:
反射需要掌握的内容:
- 什么是反射:java.lang.reflect提供的一组高级工具,能够动态查看类的所有成员及这些成员的具体信息,能够动态创建类的实例,能够动态调用类的方法
- 反射的核心类及常用的API:
- Class
- Field
- Method
- 能够利用反射API实现:
- 能够动态查看类的所有成员及这些成员的具体信息
- 能够动态创建类的实例
- 能够动态调用类的方法
JDK新特性
JDK1.5新特性
1. 静态导入
-
把静态信息进行导入直接使用(优先加载)
//静态导入 import static java.lang.Math.*; public class StaticImportDemo{ public static void main(String[] args){ System.out.println(pow(1,2)); System.out.println(random()); } }
2. 可变参数
-
可以接收多个参数传入
-
底层根据数组来接收参数值
-
可变参数只能出现在参数列表最右边
public class VriableDemo{ public static void main(String[] args){ System.out.println(sum(5.6,1,2)); System.out.println(sum(1,2,3)); System.out.println(sum(1,2,3,4,5)); } //求和方法 //数据类型...---可变参数(可以接收多个参数传入) //可变参数底层是由数组实现的,接收的参数值都会赋值到对应数组元素中 //可变参数只能出现在参数列表的最右边 public static int sum(double d,int...i){ int sum=0; for(int j=0;j<i.length;j++){ sum+=i[j]; } return sum; } /*public static int sum(int i,int j,int z){ return i+j+z; } public static int sum(int[]i){ int sum=0; for(int j=0;j<i.length;j++){ sum+=i[j]; } returnsum; }*/ }
3. 枚举
-
把值一一列举
-
列举出枚举常量,保证出现在首行并且同一行
-
构造方法私有化
-
可以定义属性和方法
-
可以定义抽象方法
-
switch表达式类型支持枚举类型
public class EnumDemo{ public static void main(String[] args){ Season spring=Season.spring; switch(spring){ case spring: System.out.println("春游"); break; case summer: System.out.println("游泳"); break; case autumn: System.out.println("爬山"); break; case winter: System.out.println("滑雪"); break; } } } //枚举类 enum Season{ //spring等效为public finalstatic Season spring=new Season(); //列举的都是枚举常量,要在首行并且要在同一行 spring{ @Override public void n(){} },summer{ @Override public void n(){ } },autumn{ @Override public void n(){ } },winter{ @Override public void n(){ } }; //定义私有化构造方法 private Season(){} //定义属性 int age; //定义方法 public void m(){} //定义抽象方法 public abstract void n(); } //代表季节的类---只能产生四个对象---一年只有四个季节 /* class Season{ //构造方法私有化 private Season(){} //创建四个对象---四个季节 public final static Season spring=new Season(); public final static Season summer=new Season(); public final static Season autumn=new Season(); public final static Season winter=new Season(); }*/
JDK1.8新特性
1. 接口可以定义实体方法
2. Lambda表达式
- Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法
lambda 表达式的语法格式如下:
(parameters) -> expression
// 或
(parameters) -> { statements;}
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
public class LambdaDemo{
public static void main(String[] args){
/*//Lambda表达式,实现函数式接口重写抽象方法
//Calc c=(int i,int j)->{return i>j?i:j;};
//参数类型省略,当重写方法方法体只有一句话可以省略return和大括号
Calc c=(i,j)->i>j?i:j;
//
System.out.println(c.max(1,2));*/
//当参数只有一个时省略()不写
//ArraySort a=arr->Arrays.sort(arr);
//当只操作一个参数值时
//::代表传递静态方法
ArraySort a=Arrays::sort;
int[] arr1={5,1,3,8,4};
a.sorted(arr1);
System.out.println(Arrays.toString(arr1));
}
}
//表示操作数组接口
interface ArraySort{
//给数组进行排序
void sorted(int[] arr);
}
//函数式接口---只有一个抽象方法的接口
interface Calc{
//求两个数大小
//抽象方法
int max(int i,int j);
//求和
//实体方法
public default int sum(int i,int j){
return i+j;
}
//求乘积
//实体方法
public static int cj(int i,int j){
return i*j;
}
}
3. Stream(操作集合流式结构 提供大量的函数式接口)
public class StreamDemo{
public static void main(String[] args){
List<String> list=new ArrayList<>();
list.add("C");
list.add("C#");
list.add("JAVA");
list.add("Python");
list.add("C++");
list.add("GO");
//筛选出以C开头内容
/*for(Stringl:list){
//判断是否以C开头
if(l.startsWith("C")){
System.out.println(l);
}
}*/
Stream<String>s=list.stream();
//筛选数据
/*s.filter(new Predicate<String>(){
//指定过滤规则
@Override
public boolean test(String s){
return s.startsWith("C");
}
}).forEach(new Consumer<String>(){
@Override
public void accept(String s){
System.out.println(s);
}
});*/
//通过Lambda表达式来写(筛选出以C开头并且降序排序)
s.filter(s1->s1.startsWith("C")).sorted((str1,str2)->str2.compareTo(str1)).
forEach(s2->System.out.println(s2));
}
}