文章目录
网络编程
B/S
C/S
数据是通过网络进行传输的
网络的基础
网络模型:
OSI:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
TCP/IP:应用层(HTTP/数据)、传输层(分组,端到端/TCP、UDP,数据包)、网络层(路由、IP,数据帧)、网络接口层(010101)
建立连接:经过3次握手
1、客户端发送请求
2、服务器响应,告诉客户端、我收到请求
3、客户端发送确认请求
断开连接:经过四次握手
提供的是可靠的数据传输。传输速度有限。端到端的数据传输。
使用场景:传输的数据要求可靠。比如发送图片等文件
UDP协议:无连接的。发送的数据,直接发,不管接收方是否收到的,也不确保收到的数据是否完全。
不可靠。传输速度很快。支持一对一、一对多、多对多的数据传输。
使用场景:对传输速度要求高,对结果不可靠影响不大的。比如腾讯会议
IP:IP地址:网络上的主机的唯一标识
IPV4:4个字节 172.16.10.115
IPV6:16个字节
域名:www.baidu.com---dns----xx.xx.xx.xx
端口号:对软件的逻辑标识
系统都会默认分配一个端口号
程序也可以自己指定端口号
0-65535 0-1024已被占用
常用程序的端口号:MySql 3306 SqlServer 1433 Oracle 1521
软件-通过网络-软件
编程:jdk ---java.net
TCP
客户端
Socket 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点
操作的是字节流
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
InputStream getInputStream() 返回此套接字的输入流。
OutputStream getOutputStream() 返回此套接字的输出流。
void shutdownOutput() 禁用此套接字的输出流。
对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列
void close()关闭此套接字。
服务端(应该先启动,否则客户端会报异常ConnectException)
ServerSocket 此类实现服务器套接字。服务器套接字等待请求通过网络传入
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。此方法在连接传入之前一直阻塞。
Socket accept() 侦听并接受到此套接字的连接。
服务器端没有自己的字节流,那怎么用?他就用客户端的流和相应的客户端进行交互
客户端-服务器 多-1
A input output
B input output
C input output
void close() 关闭此套接字
文件上传
Client--->Server
Client
C:\Users\86136\Desktop\img\1.jpg
1、把文件读到内存 FileInputStream
2、内存通过网洛传输到Server OutputStream
Server
1、读Socket InputStream
2、写入文件 FileOutputStream
文件下载
Client<---Server
Server
C:\Users\86136\Desktop\img\1.jpg
1、把文件读到内存 FileInputStream
2、内存通过网洛传输到Client OutputStream
Client
1、读Socket InputStream
2、写入文件 FileOutputStream
UDP
线程池
为什么要线程池?(容器,集合)
1丶线程池,统一管理线程,减少创建线程,销毁线程的操作,线程的使用率就高
2丶定时,定期执行一定的任务
怎么使用?
都在包java.util.concurrent
顶级接口Executor执已提交的Runnable
子接口ExecutorService
实现类:ThreadPoolExecutor
接口ScheduleExecutorService
实现类:ScheduledTreadPoolExecutor
工具类Executors
创建线程池对象
static ExecutorService newCachedThreadPool() 创建可缓存线程的线程池
static ExecutorService newFixedThreadPool(int nThreads) 创建固定线程数的线程池
static ScheduledExecutorService newScheduledThreadPool(int corePools
static ExecutorService newSingleThreadExecutor() 创建单线程的线程池
package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
ExecutorService pool= Executors.newCachedThreadPool();//可缓存线
// ExecutorService pool= Executors.newFixedThreadPool(3);//固定
// ExecutorService pool= Executors.newSingleThreadExecutor();//单一
for(int i = 1 ;i <=10 ;i++) {
pool.execute(new MyRunnable(i));
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyRunnable implements Runnable {
int num;
public MyRunnable(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " ");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package pool;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TestSchedulePool {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
System.out.println("开始执行");
pool.scheduleWithFixedDelay(new MyRunnable(1), 1, 3, TimeUnit.SECONDS);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
自定义线程池对象
public ThreadPoolExecutor(int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 最大空闲时间
TimeUnit unit,最大空闲时间 使用的单位
BlockingQueue<Runnable> workQueue,阻塞队列,等待的队列
ThreadFactory threadFactory, 线程工厂 ,创建线程
RejectedExecutionHandler handler)拒绝策略
多线程
进程:一个应用程序的实例。当我们启动一个应用程序的时候,就创建该程序的实例,其实开启了一个进程
就会分配内存,硬盘,摄像头等等这些电脑资源
进程是资源分配的基本单位
线程:运行在进程之中。进程中可以开启线程。
一个进程中至少一个有一个线程,通常叫做主线程。main方法就是主线程的入口。
一个进程中可以有多个线程,除了主线程之外,其他的线程都是有主线程创建的
玩游戏:1丶游戏 2丶背景音乐 3 丶聊天 4 丶得分
用户的感觉,多个线程在同时进行---实际上,-交替进行
线程是CPU进行调度/运行的基本单位
多线程的好处:医生看病 张医生 1234567
1丶CPU的利用率比较高
2丶用户的体验好
3丶简化开发
多线程的实现:
创建线程丶开启线程
java.lang.Tread
(一)**一种方法**是将类声明为Thread的子类。该子类应重写Thred类的run方法
run()---线程体,在其中描述线程要执行的任务
start()---使该线程开始执行;Java虚拟机调用该线程的run方法
多线程交替运行的结果:交替的获取CPU的时间片
(二) **另一种方法**是声明实现Runnable接口的类,该类然后实现run方法
比较:1丶前者通过继承
简单
继承是单继承--局限
2丶后者是通过接口,重写run方法
接口时可以多实现--灵活
实现资源共享
线程的调度:如何去给多个线程分配CPU
是个复杂的过程,外界环境的干扰
影响CPU调度的因素
1丶优先级 1- 10之间数字 默认值是5
获取CPU的概率会高
2丶 Thread.sleep(毫秒);休眠
调用此方法,该线程会进入休眠状态,(阻塞),当休眠时间结束,线程继续运行
可能引发InterruptedException
使用场景:模拟任务消耗的时间
3丶 join()加入,插入,插队
实例方法,谁调用谁插队,等t线程执行完,其他线程才执行
可能引发InterruptedException
4丶Threa.yield() 礼让
该代码所在线程礼让
放弃本次抢占到的时间片,重写进入下一次的抢占,可能礼让成功,也可能礼让不成功
package c;
public class Test {
/*
* 模拟医生看病的过程
*
* 普通号50个
* 专家号10个 用时是普通号的2倍 优先级比普通号高
*
* 等普通号看到第10个时,等所有的专家号叫完,再叫普通号
*
* --->>主线程做普通号,自定义线程做专家号
*/
public static void main(String[] args) {
Thread t = new Thread(new Special());
t.setPriority(8);
t.start();
Thread main = Thread.currentThread();
main.setPriority(2);
for (int i = 1; i <= 50; i++) {
System.out.println("----普通号:" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 10) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package c;
public class Special implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("专家号:" + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
-----------------------------------------------
package d;
public class Test {
/*
* 模拟火车站卖票
* ticket
* 多个窗口在卖
* 多个窗口可以用多个线程来模拟
* 卖票 -- 任务是一致
* 多个窗口共享10张ticket
*
* 问题:1.多个窗口卖出了同一张票
* 2.有些票没人卖
*
* -----读写不一致
* 多线程共享资源的时候,才可能引发
*
*解决方案:
* 加锁--把读写作为一个整体加锁
* synchronized(对象锁){
*
*
*/
public static void main(String[] args) {
SellTicket ticket = new SellTicket();
Thread window1 = new Thread(ticket,"窗口1");
Thread window2 = new Thread(ticket,"窗口2");
Thread window3 = new Thread(ticket,"窗口3");
window1.start();
window2.start();
window3.start();
}
}
package d;
public class SellTicket implements Runnable {
int ticket = 10;
int num = 0;
@Override
public void run() {
while (ticket > 0) {
synchronized (this) {
// 1.修改数据
ticket--;
num++;
//时间差
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 2.出票
System.out.println(Thread.currentThread().getName()+"卖出第" + num + "张票,剩余" + ticket + "张票");
}
}
}
}
多线程共享资源,会引发数据不一致的问题
解决方案:
加锁--把读写作为一个整体加锁
同步代码块
synchronized(对对象锁){ }
每一个对象都有唯一的一把锁
同步方法
一个方法内部只有一个同步代码块,那么我们经常写成同步方法,也就是把synchronized加在方法上
两者的区别:
1丶一个方法内部可以出现多个同步代码块,同步代码块可以嵌套
2丶一个线程在访问同步方法,其他线程都不能访问所有的同步方法,只能访问非同步的方法
3丶同步代码块更灵活,同步方法可以重用
同步特点:
1丶线程安全(多线程访问下,数据是正确的)
2丶效率低(不提倡把耗时的操作加同步)
编程的原则: 正确第一,效率第二
线程安全相关的类
StringBuilder 非线程安全:效率高,适合单线程
StringBuffer 线程安全的:效率低,适合多线程
HashMap 非线程安全 key可以null
Hashtable 线程安全 key不能为null
ArrayList 数组列表 非线程安全
Vector 向量 线程安全
——————————————————
死锁
在多线程中,每个线程都拥有对方想要的锁,同时又等待对方给他锁
小花 拿着洋娃娃 --你把你的小汽车给我,我就把我洋娃娃给你
小明 拿着小汽车--你把你的洋娃娃给我,我就把我小汽车给你
死锁在程序中的表现:卡死了。
解决方案:使用外力。
原因:同步代码块的嵌套
同步代码块可能产生死锁,但不是一定产生死锁
package e;
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new Xiaoming());
Thread t1 = new Thread(new Xiaohua());
t.start();
t1.start();
}
}
package e;
public class Xiaoming implements Runnable {
@Override
public void run() {
synchronized (Toy.car) {
System.out.println("小明玩小汽车");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Toy.yww) {
System.out.println("小明想玩洋娃娃,玩到了");
}
}
}
}
package e;
public class Xiaohua implements Runnable {
@Override
public void run() {
synchronized (Toy.yww) {
System.out.println("小花拿到洋娃娃了");
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Toy.car) {
System.out.println("小花想玩小汽车呢");
}
}
}
}
public class Toy {
public static final Object car = new Object();
public static final Object yww = new Object();
}
上述代码产生了死锁,改进如下代码,创建同一个对象,注意不要new
package e;
public class Test {
public static void main(String[] args) {
Object yww= new Object();
Object car = new Object();
Thread t = new Thread(new Xiaoming(car,yww));
Thread t1 = new Thread(new Xiaohua(car,yww));
t.start();
t1.start();
}
}
package e;
public class Xiaoming implements Runnable {
private Object car;
private Object yww;
public Xiaoming(Object car, Object yww) {
this.car = car;
this.yww = yww;
}
@Override
public void run() {
synchronized (car) {
System.out.println("小明玩小汽车");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (yww) {
System.out.println("小明想玩洋娃娃,玩到了");
}
}
}
}
package e;
public class Xiaohua implements Runnable {
private Object car;
private Object yww;
public Xiaohua(Object car, Object yww) {
this.car = car;
this.yww = yww;
}
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (yww) {
System.out.println("小花拿到洋娃娃了");
synchronized (car) {
System.out.println("小花想玩小汽车呢");
}
}
}
}
————————————————————
生产者和消费者的问题
生产者生产,消费者消费
规则:先生产再消费,生产后即消费,消费完后再生产。按需生产,生产则消费
生产-消费-生产-消费-生产-消费
wait();//等待,等待当前对象调用notify或者notifyAll,否则就出于阻塞状态
notify();//唤醒当前对象,解除阻塞
package f;
public class Test {
public static void main(String[] args) {
Production pro = new Production();
Thread p = new Thread(new Productor(pro));
Thread c = new Thread(new Consumer(pro));
p.start();
c.start();
}
}
public class Productor implements Runnable {
private Production pro;
public Productor(Production pro) {
this.pro = pro;
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
pro.set("名称" + i, "描述" + i);
}
}
}
public class Consumer implements Runnable {
private Production pro;
public Consumer(Production pro) {
super();
this.pro = pro;
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(pro.getName()+" "+pro.getDesc());
pro.get();
}
}
}
public class Production {
private String name;
private String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
boolean flag = true;
public synchronized void set(String name, String desc) {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setName(name);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
setDesc(desc);
flag = false;
notify();
}
public synchronized void get() {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " " + getDesc());
flag = true;
notify();
}
}
包与权限
包相当于操作系统的文件夹
好处:
- 管理java文件的:方便寻找(包名+类名)、解决重名的问题
- 保护资源 (结合着访问控制符,来限定资源的访问)
包的使用 :
- 一般都用小写英文
- 见名之义 公司域名的倒写+ 【部门名称】+项目名称+模块 、用.分隔,不能以.开头
- www.taobao.com---- com.taobao+qianniu
学生管理系统 com.openlab.student.entity
包的创建:
- new Package
- 创建类的同时创建包 com.openlab.student.util StudentUtil.java
包的声明:
- package com.openlab.student.entity;
- 必须位于类的第一行非注释语句
包的导入:
- import java.util.Scanner;–类的完全限定名
- import java.util.* --导入包下的所有java类,但是不会导入子包中的类
- 不同包下的资源相互使用,需要先导包
- 快捷键 ://代码提示 Alt+/
- //导包 Ctrl+Shift+O
- 导入的类重名,来自不同的包,需要显式的写出 :
- java.util.Date dateUtil;
- java.sql.Date dateSql
权限:
- public 共有的 类内部、同包、不同包
- protected 受保护的 类内部、同包、不同包的子类
- default 默认的 类内部、同包
- private 私有的 类内部(ok) 同包(否) 不同包(否)
- public>protected>default>private
枚举
enum
- 可以当成数据类型来用
- 管理一系列的常量(限定取值)
由来:
- 枚举出来之前都是拿class或者interface来组织管理常量—基本的数据类型
- 缺点:只要数据类型合适,就会编译通过,不考虑实际的业务,可能造成错误
enum 限定取值
本质是Enum的子类:不能再继承别的类
public final String name() {
return name;
}
public final int ordinal() {//序号
return ordinal;
}
自定义枚举
public enum Role2 {
ROLE_NOMAL,
ROLE_VIP,
ROLE_SUPER_VIP
}
可以当成数据类型来用
private Role2 role;
限定取值
user.setRole(Role2.ROLE_NOMAL);
枚举值的比较 equals或者==都可以
Role2 r=user.getRole();
if(r==Role2.ROLE_SUPER_VIP) {
System.out.println("超级用户。。。。。。。。。。。。");
}else if(r==Role2.ROLE_NOMAL) {
System.out.println("普通用户。。。。。。。。。。。。");
}
用switch更方便
switch(r) {//byte int short String(1.7+),enum
case ROLE_SUPER_VIP:
System.out.println("超级用户。。。。。。。。。。。。");
break;
case ROLE_NOMAL:
System.out.println("普通用户。。。。。。。。。。。。");
break;
case ROLE_VIP:
System.out.println("VIP用户。。。。。。。。。。。。");
break;
}
常用类解析
常用类 jdk给提供出来的一些类。
为什么要学习?我们直接用,不用重复造轮子。
怎么学?
- 如果静态的,类名.成员
- 不是静态的,怎么创建实例对象,哪些属性,哪些方法(方法名,参数列表,返回值–功能)
- 使用的层次----> 面向对象的思想
- 工具?javadoc注释----查看api帮助文档
- IDE --提示,注释
StringBuffer类、StringBuilder类
AbstractStringBuilder 父类 char[] value 可变的 自动扩容
StringBuffer类
- 线程安全的可变字符序列。synchronized 同步锁 线程安全—适合多线程
- 可变字符序列,一个类似于 String 的字符串缓冲区
- StringBuffer append(XXX) 往末尾添加
- StringBuffer insert(int offset, String str) 指定位置添加
- StringBuffer reverse() 反转
- String toString()
StringBuilder类
- 一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,
- 但不保证同步 ,非线程安全—适合单线程
- String 类 其值在创建之后不能更改,可以共享 本质 是char value[]
- String str=“abc”; 内存的方法区有一个字符串常量池,先去判断常量池中是有"abc",如果没有,就创建一个,放在常量池中,把地址给str
- String str2=“abc”; 如果有,直接把地址赋值给str2,str、str2共同指向同一个地址,这就叫共享。
同时,带来了一个问题。
str=str+“cba”; — “cba” 、重写开辟区域,存储新的对象"abccba"
str=str+",a";— “,a”、、重写开辟区域,存储新的对象"abccba,a"
“cba”、 ",a"中间对象,垃圾
所以,在需要频繁地修改字符串的内容的时候,不适合使用String类
String str=new String(“abc”);
基本数据类型包装类
基本数据类型(8)
- 数值型:byte、short、int、long、float、double
- 字符型:char
- 布尔型:boolean
基本数据类型都是存储栈中,栈中存储的就是值本身,赋值、参数传递的时候操作的也是值本身。
Java是面向对象的,对象都有属性和方法,基本数据类型只有值,没有方法,不遵循面向对象。Java为了完善,为了提供更多的方法去操作基本数据数据,就提供出来了包装类。
包装类:
byte— Byte
short—Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
除了int–Integer、char–Character,其它的都是首字母变大小
包装类的继承关系
Object
–Number(abstract父类)
–Byte、Short、Integer、Long、Float、Double
–Character
–Boolean
Integer以此为例
public final class Integer extends Number implements Comparable
1、final 不能继承
2、extends Number 未继承很多功能
3、Comparable 可比较的 Arrays.sort()
public int compareTo(T o);
返回值:负数:小于 正数:大于 0:等于
属性 field
MIN_VALUE -2147483648 -2^31
MAX_VALUE 2147483647
TYPE 获取int类对象
SIZE = 32; int数据占32位
BYTES = SIZE / Byte.SIZE int数据占多少字节
程序中使用的话,直接用常量,可以节约资源
private final int value; 存储的就是Integer类的数据,本质上就是int value
value一旦赋值,不能更改,靠什么达到像String对象的共享?—稍后
构造
Integer(int value)
Integer(String s)---要求s必须能转换成int,否则NumberFormatException: For input string: "a25"
方法
static int parseInt(String s)
static int parseInt(String s, int radix) 就是把字符串转换成int ,radix进制可选
不支持Integer.parseInt("0x10",16) 但是Integer.parseInt("10",16)
------------------------------------------------------------------
static Integer valueOf(String s) 、
static Integer valueOf(int i)
valueOf系列方法,把其它数据类型转换成当前类型
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
以上方法意思就是,如果传入的整数是-128-high之间的数,直接从缓存中返回对象。否则新创建
原来,Integer类中有一个静态内部类 IntegerCache,内部类中有一个cache,当Integer加载的时候,就会初始化此类,缓存
-128-high(一般就是127)之间的数。
如果你需要Integer类型的对象,优先使用valueOf,而不是构造方法
**其它包装类?**
public static Float valueOf(float f) {
return new Float(f);
}
Float、Double类的valueOf()没有缓存。为什么不呢?[-128-127] 整数数量是固定的,小数是无穷的。
**Boolean类**
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
总结:
1、Byte、Short、Integer、Long的valueOf方法都有缓存,【-128,127】
2、Float、Double的valueOf方法没有缓存,每次都会新创建对象
3、Character 的valueOf方法 有缓存,【0,127】
4、Boolean 的valueOf方法 有缓存,有静态的常量true,false,直接返回
-------------------------------------
xxx(基本数据类型) xxxValue() 获取包装类中的基本数据类型的值 value
-------------
toString()
static String toString(int i)---建议看源码
------------
equals方法主要来判断值是否相等
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Integer 中equals方法:当类型相同且值相等,才返回true
-----------------
进制间转换 把10进制的数转换成其它进制
static String toHexString(int i) --16进制
static String toOctalString(int i) --8进制
static String toBinaryString(int i)--2进制
------
static int reverse(int i) 反转 不是这样的效果123---321
总结:1、数据类型转换
int--String
1)a+""
2)String.valueOf()
3)Interger:String toString(int i)
String--int
1)Integer.parse(String)
Integer-String
1)toString()
String--Integer
1)Integer valueOf(String s, int radix)
2)Integer(String s)
int--Integer
1)Integer(int i)
2)Integer valueOf(int i)
Integer--int
1)int intValue()
2、进制转换
------------------------
装箱:基本数据类型--->包装类型 自动转换
本质:Integer.valueOf()
Integer i=12;
Integer i2=Integer.valueOf(12);
以上代码效果相同
---
Integer num1=12;
Integer num2=12;
System.out.println(num1==num2);//true
说明num1和num2地址相同,因为num1和num2都是从缓存中取的同一个地址
--
Integer num1=129;
Integer num2=129;
System.out.println(num1==num2);//false
129没有缓存,每次都需要重写创建,所以num1和num2地址不同
--
Double num1=12d;
Double num2=12d;
System.out.println(num1==num2);//false
因为Double 没有缓存
拆箱:包装类型--基本类型 自动转换
本质:Integer.intValue()
int num=i;//拆箱
注意:如果可以的话,不要频繁地装拆箱
什么时候装箱、拆箱?
Integer num1=400;
int num2=400;
System.out.println(num1==num2);//true
返回true就可以说明num1发生了拆箱操作。换一句话来说,==链接包装类和基本数据类型的时候,就拆箱
Integer num1=400;
int num2=400;
System.out.println(num1.equals(num2));//true
返回true就可以说明num2进行了拆箱
Long num1=400l;
int num2=400;
System.out.println(num1.equals(num2));//false
原因:long类型的equals方法要求类型相同且值相同才返回true,而num2自身装箱成了Integer类型
Integer num1=400;
int num2=400;
System.out.println(num1*num2);
当+、-、*、/、%链接包装类和基本数据类型的时候,是拆箱
拆箱的时候注意避免空指针异常
Integer num1=null;
int num2=num1;//java.lang.NullPointerException
就是拆箱实际在执行intValue()
Character
System.out.println(Character.isDigit('a'));
System.out.println(Character.isLetter('a'));
System.out.println(Character.isWhitespace(' '));
System.out.println(Character.isUpperCase('A'));
System.out.println(Character.isLowerCase('b'));
System.out.println(Character.toLowerCase('U'));
BigInteger
做权限控制
BigInteger setBit(int n) bi+2^n
boolean testBit(int n)
BigDecimal
异常:
在程序编译或运行过程中出现的不正常的情况
从面向对象的角度来说,异常也是一个对象。如何描述一个异常?
异常名称:java.lang.ArithmeticException
异常出现的原因:/ by zero
异常出现的位置:at Test.main(Test.java:5)
处理异常:
方式一:添加判断 if...else
缺点:程序员会把精力放在避免异常,无法集中在业务上了。一直在不漏洞,但是也不一定能补全
业务代码被处理异常的代码淹没了,可读性不强
方式二:异常处理机制: 预置一些异常处理程序---如果异常情况---执行相应的处理程序,这样就不会让程序中断
小明上班 大约30分钟 ---正常
堵车----迟到 ,处理措施:通知领导
交通事故----处理措施:请假一天
生活继续
具体怎么实现?通过以下几个关键字来实现的:try catch finally throws throw
try{
////可能出现异常的代码
}catch(){
//异常出现的话,如何处理---异常处理程序
}catch(){
//异常出现的话,如何处理---异常处理程序
}
finally{
//不管是否出现异常,都会去执行的代码
//关闭资源 善后、收场
}
此结构中,try必选,catch和finaly可选,但是必有其一
正常情况:执行try,继续后续代码
异常情况:出现异常之后的代码不再执行,转到catch块执行,继续后续代码
出现异常,系统会产生一个异常对象,会和catch块中捕获的类型匹配,匹配上才执行其中的代码,匹配不上就报错
多种异常的话,我们可以使用多个catch语句来捕获。从上往下匹配,上面的匹配上,下面的就不再匹配
所以,catch种捕获的异常类型应该从小到大来写,否则会报错,因为有些catch块永远也执行不了
提问:
1、直接一个父类异常把各种子类异常全部包含了。从而只写一个catch块行吗?
可以的,只是不具体。不能清晰地去处理某一种具体的异常。
2、如果某些异常的处理程序是一样的,可以用|来连接多个异常类型
catch(InputMismatchException|ArithmeticException ex )
finally不管是否出现异常,都会去执行的代码
return 无法阻拦
当有return语句的时候,先执行return前的语句,再执行finally,最后return
有没有可以阻拦finally执行的?有一个 ,System.exit(status),直接停掉JVM
status如果是0,表示正常退出。非0,表示异常退出。
提问:
如果之前return了然后执行 finally语句 finally语句里面也有有return 那么执行哪个return
回答:
最终是finally中的return起了作用
一般编码中,不会同时出现。不建议这样写。
异常体系:
Throwable
Throwable 类是 Java 语言中所有错误或异常的超类。
通过 Java 虚拟机或者 Java throw 语句抛出。
只有此类或其子类之一才可以是 catch 子句中的参数类型
void printStackTrace()打印异常的堆栈信息 ---方便调试程序的
String ex.getMessage()获取异常的原因 ---方便调试程序的
Error 严重的问题,合理的应用程序不应该试图捕获
我们程序不用关注这一类
合理的应用程序不应该试图捕获的严重问题
OutOfMemoryError 内存溢出--堆内存
int[] num=new int[1024*1024*1024];//.OutOfMemoryError: Java heap space
StackOverflowError 栈溢出
private static void func() {
func();
}
Exception 一般问题,合理的应用程序想要捕获
RuntimeException 运行时异常,不强制要求程序处理
ArrayIndexOutOfBoundsException 数组下标越界异常
NullPointerException 空指针异常 --null对象调用属性和方法
InputMismatchException 输入不匹配异常
ArithmeticException 算术异常
ClassCastException 类型转换异常
除了RuntimeException及子类之外,编译时异常,外界的原因,强制要求程序处理
举例:
try {
Class.forName("com.open.TestJava");//加载类
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
throws 声明异常
在方法声明后添加throws ClassNotFoundException:意思是 方法体中有可能会发生此类异常
并没有真正处理,那么谁来处理?谁调用谁处理!
什么时候用?就是可能出现异常的地方,不想或不能处理此异常,就选择声明异常。声明之后,会通过调用者逐层要求处理,最终道JVM停止。
如果存在多个异常,是可以声明多个,用逗号分隔开
运行时异常,不强制要求捕获或者声明。即使声明了,也不要求调用者必须捕获或者声明。所以,我们一般不声明,但是往往会捕获。
编译时异常,强制要求捕获或声明。如果方法内部不捕获,那么调用者或者上层调用者就必须捕获。否泽,编译会出错。
异常是要捕获还是声明?
1、如果调用者的异常处理程序是一致的,我们就选用方法内部捕获
2、还要考虑处理此异常的层级。
重写中异常声明的问题:
子类重写父类的方法,规则是:
1、继承
方法名相同
参数列表相同
返回值类型可以缩小
访问控制权限可以放大
声明的异常可以缩小
重载:
1、同一个类中
2、方法名相同
3、参数列表不同
4、与返回值类型和访问控制权限无关
--------------------
throw 抛出异常
throw 异常对象;
自定义异常
如果是编译异常,可以直接继承Exception
运行时异常,可以继承RuntimeException。或者意思的某个子类
可以重写带参构造,设置异常原因。
自定义异常只能手动抛出,不能让JVM抛。
综合案例:
模拟老师用电脑的上课
异常可以转换,转换的时候可以传递 -------在catch块中 throw new ClassInterruptException(e);
com.teach.ClassInterruptException: com.teach.MaoYanException: 电脑猫眼啦
at com.teach.Teacher.teach(Teacher.java:22)
at com.teach.Test.main(Test.java:8)
Caused by: com.teach.MaoYanException: 电脑猫眼啦
at com.teach.Computer.start(Computer.java:18)
at com.teach.Teacher.teach(Teacher.java:14)
... 1 more
以后提问问题的时候:
编译是否报错。---贴代码
如果编译无误,运行报错。----贴代码 ,最好带上错误堆栈信息
如果编译没错,运行也没报错,只是结果不理想。 ----业务逻辑的问题
日志记录
需要把异常的堆栈信息保存,不能只停留在控制台。从而更方便的定位错误
日志:错误日志、正常的操作信息、SQL日志、业务日志
一般使用的是Apache公司的开源项目log4j
使用步骤
1、下载jar 包 --java的class文件的压缩包
2、添加jar包项目
3、配置文件 . propreties .xml log4j.properties
propreties 以键值对方式存储 key=value
4、代码中引用
集合
如果存储多个变量,可以使用数组。
数组特点:定长、存储的是同种类型 、存储单列数据
总结缺点:就是没有很多的增删改查、统计等等这样的方法。
学号—学生
科目号–课程信息
订单编号—订单信息
映射关系 key---value
集合框架:一些接口和类。java.util包下
学习方法:学习接口,使用实现类
Collection 集合 单列集合的根接口
一些 collection 允许有重复的元素,而另一些则不允许。
一些 collection 是有序的,而另一些则是无序的
JDK 不提供此接口的任何直接 实现
List 列表
有序、访问顺序和插入顺序一致
有索引
允许重复
允许null 元素
使用参考:动态数组
ArrayList:可变长的数组
此实现不是同步的,不是线程安全
好处: 访问、修改速度快
缺点: 添加、删除速度慢
浪费空间
使用场景:查询、遍历
如果需要频繁增删,就不要考虑了
LinkedList:双向链表
随机访问效率低
增删快
Set 数据集,集合
不包含重复元素
最多包含一个 null 元素
不要求有序
参考:模仿了数学上的 set 抽象
HashSet
LinkedHashSet
TreeSet
Map 映射 --映射
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
HashMap
工具类:Collections
泛型 可以约束集合存储的数据类型
数据类型参数化
字母:E(element) K(key) V(value) T(type)
其实所有的字母都可以使用
适用的场景:泛型类、泛型接口、泛型方法
好处:扩展性好
集合Collection
List
使用:相当于动态的数组
允许重复,有序(访问顺序和插入顺序一致)
允许为null
有下标
ArrayList:
真正的动态数组,自动地扩容或缩容
线程不安全
底层实现:数组
特点:随机访问快、增删慢、要使用连续空间,空间利用率不高
使用场景:如果需要频繁地增删,不要使用
LinkedList:
底层实现:双向链表
特点:增删快,随机访问慢
作为List的实现类:ArrayList可以实现的功能,LinkedList都可以
作为Deque的实现类:
Deque:双端队列
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E getLast()
E getFirst()
还可以作为栈来使用
void push(E e);//给开头添加元素
E pop();// 获取并移除开头的元素
E peek();获取不移除开头的元素
还可以作为普通队列使用
boolean offer(E e);//给结尾添加元素
E poll()// 获取并移除开头的元素
E element() //获取不移除开头的元素
Vector 实现 list接口
和ArrayList很像
最大的区别就是,Vector是线程安全的
在单线程下,就被ArrayList替代了
Stack 栈
被LinkedList替代了
Set
模拟了数学的集合的概念
不允许重复
不要求有序
没有下标
HashSet
不允许重复
无序
* hashSet 无序 不重复
* 底层实现
* 1、jdk8之前 数组+链表
* jdk8之后 数组+链表/红黑树
*
* 当添加元素的时候
* 1)根据hashCode去分组----数组
* 2) 如果hashCode值相同(哈希冲突),去判断对象的值是否相同(equals)
*
TreeSet
底层实现:红黑树--平衡二叉树
有序的(不是插入顺序,自定义顺序)
不重复
Map 关系、映射
Entry 实体 Key -- Value (一对一,多对一)
将键映射到值的对象
一个映射不能包含重复的键,键唯一
每个键最多只能映射到一个值
多列集合:Key的集合(Set),Value的集合(List),Entry集合
HashMap
允许使用 null 值和 null 键
Collections 操作集合的工具类
Arrays 操作数组的工具类
File
File表示文件、文件夹
如果操作文件的内容,就需要用到流
流 --是内存和硬盘数据交互的一个通道/桥梁 java.io
按照数据的流向,把流分为
输入流:文件-内存(读)
输出流:内存-文件(写)
站在内存角度上来理解
按照数据交互的单元来划分,可以分为
字节流:一次一个字节
字符流:一次一个字符
字符流最终还是以字节流方式实现的
综合起来:字节输入流 字节输出流
字符输入流 字符输出流
字节流
字节输入流
InputStream 此抽象类是表示字节输入流的所有类的超类。
abstract int read() 从输入流中读取数据的下一个字节. 返回值:下一个数据字节;如果到达流的末尾,则返回 -1。
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -1。
int read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
void close() 关闭此输入流并释放与该流关联的所有系统资源。
FileInputStream 从文件系统中的某个文件中获得输入字节
FileInputStream(File file)
FileInputStream(String name)
BufferedInputStream 在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
字节输出流
OutputStream 表示输出字节流的所有类的超类
abstract void write(int b) 将指定的字节写入此输出流。
void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
void close() 关闭此输出流并释放与此流有关的所有系统资源。
void flush() 刷新此输出流并强制写出所有缓冲的输出字节。
FileOutputStream 文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
BufferedOutputStream 该类实现缓冲的输出流
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
不管是二进制文件还是文本文件,在内存都是以二进制的方式存储
字符串—byte[] 编码 就依赖一定的编码方式 不同的编码方式,存储方式不一样
字符流–转换 不管是字节流,还是字符流,最终都是以字节流的方式在处理的
字符输入流
Reader 用于读取字符流的抽象类
int read() 读取单个字符。
int read(char[] cbuf)将字符读入数组。
abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
InputStreamReader 字节--->字符 字节流通向字符流的桥梁
自带缓冲区 8192
可以自定义编码格式来解码
InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
FileReader 用来读取字符文件的便捷类。
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。
FileReader(File file)在给定从中读取数据的 File 的情况下创建一个新 FileReader。
BufferedReader 提高字符流的读取速度
String readLine() 读取一个文本行。
包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
字符输出流
Writer
void write(char[] cbuf) 写入字符数组。
abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分。
void write(int c) 写入单个字符。
void write(String str) 写入字符串。
void write(String str, int off, int len) 写入字符串的某一部分。
OutputStreamWriter 是字符流通向字节流的桥梁 字符--->字节
OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。
FileWriter 用来写入字符文件的便捷类。
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
BufferedWriter 提高字符流的写入速度
void newLine() 写入一个行分隔符。
思路:
1、字节 Stream ?还是字符? Reader/Writer
2、流向:输入input reader?输出? output writer
3、如果需要指定编码格式去转换字节–字符,转换流 InputStreamReader,InputStreamWriter
4、是否需要提高速度?Bufferedxxx
IO流
字节流
字符流
操作二进制文件,操作java基本数据类型
DataInputStream
DataOutputStream
操作java任何数据类型,专门来操作Object对象
ObjectInputStream
Object readObject() 从 ObjectInputStream 读取对象。
ObjectOutputStream
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
不能序列异常
java.io.NotSerializableException: com.data.Student
序列化:java对象 ---->字节
实现方式一:实现Serializable接口 ,此接口只是一个标记,表名此类型的对象可以序列化
1、也可以实现部分属性的序列化 ,不需要序列化的字段前面加上transient
不需要的情况:1、业务 2、自定义类型比较复杂,代价大
2、自定义类型 序列化时,要求所有的非transient类型的属性都实现序列化
3、父类实现了序列化,子类可以继承,无需再实现此接口
4、静态的属性也是不序列化的,静态的内容是属于类的
实现方式二:实现Externalizable接口
可以实现部分属性的序列化
作用:主要是方便存储对象,或者方便在网络间传输。
反序列化:字节---->java对象
readObject()
可能会出现InvalidClassException,原因是因为serialVersionUID不一致了
serialVersionUID 识别序列化字节和内存中类的版本的
根据类中的属性和方法综合计算来的一个long值,也就是类中属性或者方法有改动,long值就会改变
serialVersionUID的作用:主要用在序列化列升级的时候(属性和方法有改动的时候)
1)如果让当前版本兼容之前的版本,需要使用之前的序列号
2)如果不想让当前版本兼容之前的版本,改变序列号
作用:方便恢复/重建对象
Properties 集合
log4j 日志记录 .properties 键值对 xxx==xxx
java中很常用的配置文件 properties/xml
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
继承Hashtable<K,V>,Hashtable和HashMap类似,唯一的区别就是Hashtable是线程安全的
属性列表中每个键及其对应值都是一个字符串。
String getProperty(String key)
setProperty(String key, String value)
void store(Writer writer, String comments)
void store(OutputStream out, String comments)
void load(Reader reader)
void load(InputStream inStream)
System.in----InputStream 标准的字节输入流
System.out----PrintStream print方法:以字符串的形式输出
PrintWriter 以字符串的形式输出