文章目录
1.常用类库
1.用String类对象进行字符串拼接能避免尽量避免,因为进行字符串加法拼接时,会产生很多内存垃圾。内存垃圾存储在字符串常量池中,也就是堆内存的老年代中,不会被GC回收掉。因此拼接字符串药使用:Stringbuilder和Stringbuffer这两个类对象,调用方法实现。
2.集合
1.Arraylist是数组结构,查找快,增删慢。底层是动态数组,扩容机制跟数组扩容一样。如果存储非常多的数据,建议使用带参数的构造方法,初始化集合的容量,节省扩容导致的内存占用。
2.面试题:
ArrayList集合通过无参构造方法创建时,其集合容量为0;当我们调用add方法添加元素时,会自动的进行扩容,扩容容量为10。
3.LinkedList是双向链表结构,增删快,查找慢。
4.foreach:增强for循环,用于迭代数组或者集合(单值集合Collection),foreach会自动选用最优的迭代算法(迭代器)。
5.HashSet集合:散列存放,无序存储,内部存储时利用HashMap实现的。
6.了解:Comparator接口也可以实现比较 ,Arrays.sort(T[], Comparator<? super T>)就可以传入参数为Comparator的接口,实现排序。
7.哈希表概述:对象数组+链表的结构(对象数组中包含链表)。初始桶的数量(对象数组长度):16 ,散列因子(默认加载因子):0.75(哈希表存储的容量超过0.75已经被利用时,就会进行桶的扩容)。
(hashcode)哈希值%桶的数量=哈希桶下标,根据下标存储数据。每个哈希桶的链表数量大于8时,从链表转为红黑二叉树;当哈希桶中的数据量减少到6时,从红黑二叉树转换为链表。
8.HashMap:线程不安全,效率高。HashTable:线程安全,效率低。(只有一个排队的队伍)ConcurrentHashMap:采用分段锁机制,保证线程安全,效率又比较高。(排队的队伍不止一个)
9.注意:自定义对象存储在HashMap集合的键中时,不能轻易去改变对象的属性。因为对象属性发生改变后,其存储时的HashCode值会发生改变进行重新计算,会找不到之前的对象存储的位置。
3.IO
1.文件加密或解密方法:任何数据相同的数字两次,结果就是其本身。例如:fos.write(b10);通过两次异或就可以实现加密和解密的操作。
2.字符流:用来读取单个文字为单位的数据,解决字节流只能读取字节为单位的数据。
3.utf-8编码:是一种可变化长度的字符编码,我们没办法确定一个文字由多少字节组成,而且其编码中的第一个字节兼容ASCII编码。
4.用字符输入流读取字符时,通过一次读取多个字符到char[]中保存的方式时,char[]数组里面有默认值的,默认值为空白字符。
5.使用字符输出流时,在流的管道中,会存在类似缓冲的机制,把写出的字节缓冲起来组合成单个字符,我们只有调用flush()方法,进行刷新管道,字符才会被写到硬盘中。其实close()方法在执行之前,也会自动调用flush()方法,我们也可以不写flush()方法。
6.对象以及对象的属性必须实现Serializable接口,才可以被序列化。这是一个标记的接口,里面没有任何的实现。
7.jdk 9之后,处理异常和关闭流的方法:try-with-resource
public class Demo15 { public static void main(String[] args) throws FileNotFoundException { //try-with-resources //jdk1.7之前 /*try { FileReader fr = new FileReader("c://book.txt"); int c = fr.read(); System.out.println((char)c); fr.close(); } catch (IOException e) { e.printStackTrace(); }*/ //jdk9 FileReader fr = new FileReader("c://book.txt"); PrintWriter pw = new PrintWriter("c://book.txt"); try(fr;pw){ //传入的流对象必须实现Closeable接口,这段代码自动的执行close方法 int c = fr.read(); System.out.println((char)c); }catch (IOException e) { e.printStackTrace(); } } }
4.多线程技术
1.每个线程都有自己的栈空间,共用一份堆内存。
2.让线程中断:可以在主线程中调用interrupt()方法,给线程添加中断标记,然后当主线程执行完以后,其他线程发现了此中断操作,此线程执行的任务就会跳到catch块,我们在catch块中加入关闭资源的方法,然后最后一行加上返回语句,此线程就会被中断。这种中断方式是比较合理的,而使用线程的stop方法会造成线程的不安全,可能会导致某些资源来不及释放。
package thread;
public class Demo5 {
public static void main(String[] args) {
//线程中断
//y一个线程是一个独立的执行路径,它是否结束应该由其自身决定
Thread t1 = new Thread(new MyRunnable());
t1.start();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//给线程t1添加中断标记
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000); //调用sleep方法会抛出异常,但我们只能将其catch,因为Runnable接口没有抛出异常,而且只有在使用下列中的方法时,我们才可以选择中断线程
/**
java.lang.Object#wait()
java.lang.Object#wait(long)
java.lang.Object#wait(long, int)
java.lang.Thread#sleep(long)
java.lang.Thread#interrupt()
java.lang.Thread#interrupted()*/
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现了中断标记,线程自杀");
return;
}
}
}
}
}
3.线程:分为守护线程和用户线程
用户线程:当一个进程不包含任何的存活的用户线程时,进行结束。
守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
//设置用户线程
t1.setDaemon(true);//当主线程死亡时,守护线程也会停止
4.线程不安全的问题:
多个线程执行一个任务的时候,交替抢夺到时间片,争抢同一个数据,会导致数据错误的问题。(其他线程执行完毕,但另一个线程还未执行完毕的时候,数据变量已经被其他线程改变)。
5.线程同步-线程安全解决方法:
*同步代码块:
package thread;
//线程同步synchronized
public class Demo8 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1 同步代码块
//格式:synchronized(锁对象){
//
//
// }
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//Object o = new Object(); //这里不是同一把锁,所以锁不住,多个线程必须看同一把锁。
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
}
}
}
}
}
*同步方法:
package thread;
//线程同步synchronized
public class Demo9 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案2 同步方法
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){ //当前锁是this 如果synchronized前边有static修饰,锁对象就是Ticket.class
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
return true;
}
return false;
}
}
}
*显式锁
*公平锁:多个线程排队执行,先到先得
//同步代码块和同步方法都属于隐式锁
//线程同步lock
public class Demo10 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1 显示锁 Lock 子类 ReentrantLock
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//总票数
private int count = 10;
//参数为true表示公平锁 默认是false 不是公平锁
private Lock l = new ReentrantLock(true);
@Override
public void run() {
while (true) {
l.lock(); //锁住
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
l.unlock(); //解锁
}
}
}
}
*线程死锁:我在等你,你在等我
package thread;
public class Demo11 {
public static void main(String[] args) {
//线程死锁
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
static class Culprit{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放了人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯被放了,罪犯也放了人质");
}
}
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人质,我放了你");
c.fun();
}
public synchronized void fun(){
System.out.println("警察救了人质,但是罪犯跑了");
}
}
}
*生产者与消费
//等待唤醒机制
void notify() 唤醒正在此对象监视器上等待的单个线程。
void notifyAll() 唤醒等待此对象监视器的所有线程。
void wait() 导致当前线程等待它被唤醒,通常是 通知或 中断 。
void wait(long timeoutMillis) 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。
void wait(long timeoutMillis, int nanos) 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。
package com.java.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo4 {
/**
* 多线程通信问题, 生产者与消费者问题
* @param args
*/
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
f.setNameAndSaste("老干妈小米粥","香辣味");
}else{
f.setNameAndSaste("煎饼果子","甜辣味");
}
}
}
}
//服务生
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i=0;i<100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
//true 表示可以生产
private boolean flag = true;
public synchronized void setNameAndSaste(String name,String taste){
if(flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();//唤醒当前对象下等待的所有线程
try {
this.wait();//使当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag) {
System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
*线程的状态:state
6.Callable:线程创建的第三种方式(了解课件)
Runnable 与 Callable
Runnable 与 Callable****的相同点
- 都是接口
- 都可以编写多线程程序
- 都采用Thread.start()启动线程
Runnable 与 Callable****的不同点
- Runnable没有返回值;Callable可以返回执行结果
- Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable****获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
接口定义
//Callable接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}
Callable使用步骤
- 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
- 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
- 通过Thread,启动线程
new Thread(future).start();
7.线程池:看课件(了解)
线程池 Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
线程池的好处
- 降低资源消耗。
- 提高响应速度。
- 提高线程的可管理性。
Java中的四种线程池 . ExecutorService
1. 缓存线程池
/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
ExecutorService service = Executors.newCachedThreadPool();
//向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
2. 定长线程池
public class Demo14 {
/*定长线程池
长度是指定的线程池
加入任务后的执行流程
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程
**/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
3.单线程线程池
public class Demo15 {
/*单线程线程池
执行流程
1 判断线程池的那个线程是否空闲
2 空闲则使用
3 不空闲则等待它空闲后再使用
**/
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
4.周期性任务定长线程池
public class Demo16 {
/*周期任务 定长线程池
执行流程
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程
周期性任务执行时
定时执行 当某个任务触发时 自动执行某任务
**/
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//定时执行一次
//参数1:定时执行的任务
//参数2:时长数字
//参数3:2的时间单位 Timeunit的常量指定
/* scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
},5, TimeUnit.SECONDS); //5秒钟后执行*/
/*
周期性执行任务
参数1:任务
参数2:延迟时长数字(第一次在执行上面时间以后)
参数3:周期时长数字(没隔多久执行一次)
参数4:时长数字的单位
* **/
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
},5,1,TimeUnit.SECONDS);
}
}
8.Lambda表达式
- 函数式编程思想概述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRW4hOea-1605874612518)(E:/JAVA/JAVA资料/Java基础/07.会员版(2.0)]-就业课(2.0)-异常与多线程/18.【线程池、Lambda表达式】-笔记/就业班-day07-线程池、Lambda表达式/img/03-Overview.png)
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
- Lambda的使用前提
Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的Runnable
、Comparator
接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。 - 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”
代码例子:
public class Demo17 {
/*
lambda表达式
函数式编程思想
**/
public static void main(String[] args) {
//冗余的Runnable编写方式
/* Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
});
t.start();*/
Thread t = new Thread(() -> System.out.println("锄禾日当午"));
t.start();
}
}
5-网络编程
5-1 网络编程概述
5-2 TCP程序
TCP协议的C/S程序
需要使用到两个类, 来编写TCP协议的 CS程序 .
1.ServerSocket 搭建服务器
2.Socket 搭建客户端
两方使用socket(套接字 , 通信端点) 进行交流
ServerSocket
用于创建服务器 . 创建完毕后, 会绑定一个端口号.
然后此服务器可以等待客户端连接 .
每连接一个客户端 , 服务器就会得到一个新的Socket对象, 用于跟客户端进行通信
常用构造方法:
ServerSocket(int port);
创建一个基于TCP/IP协议的服务器 , 并绑定指定的端口号.
注意: 参数port的范围是: 0-65535 (建议1025-65535)
常用方法:
Socket accept();
等待客户端连接 .
此方法会导致线程的阻塞!
直到一个新的客户端连接成功, return Socket对象后, 线程在继续执行.
void close();
释放占用的端口号 , 关闭服务器
Socket
是两台计算机之间通信的端点 , 是网络驱动提供给应用程序编程的一种接口 一套标准, 一种机制 .
构造方法:
Socket(String ip,int port)
创建一个套接字, 并连接指定ip和端口号的 服务器.
参数1. 服务器的ip地址
参数2. 服务器软件的端口号..
常用方法:
- OutputStream getOutputStream();
返回的是 , 指向通信的另一端点的输出流
- InputStream getInputStream();
返回的是 , 指向通信的另一端点的输入流
- void close();
关闭套接字
注意:
在网络编程时, 获取输入输出流的操作 ,对于客户端与服务器来说是相对的
客户端的输入流, 输入的是服务器的输出流 输出的内容.
客户端的暑促刘, 输出到了服务器的输入流中.
所以 在使用时, 需要注意以下一点规则:
客户端与服务器获取流的顺序必须是相反的:
例如:
客户端先得到了输入流 , 那服务器必须先获取输出流
案例:
服务器端:
package com.kaikeba;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SeverDemo {
// 服务器
public static void main(String[] args) throws IOException {
// 搭建服务器
ServerSocket server = new ServerSocket(55555);
System.out.println("服务器启动了");
// 等待客户端的连接
Socket socket = server.accept();
System.out.println("服务器连接了");
// 获得输出流
OutputStream op = socket.getOutputStream();
// 转换为打印流
PrintStream ps = new PrintStream(op);
// 发送消息
ps.println("欢迎连接服务器");
System.out.println("服务器程序执行结束");
}
}
客户端:
package com.kaikeba;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientDemo {
// 客户端
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 55555);
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String text = br.readLine();
System.out.println("客户端收到:" + text);
}
}
5-3 在服务器中加入多线程
package com.kaikeba;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SeverDemo {
// 服务器
public static void main(String[] args) throws IOException {
// 搭建服务器
ServerSocket server = new ServerSocket(55555);
System.out.println("服务器启动了");
while (true) {
// 等待客户端的连接
Socket socket = server.accept();
System.out.println("服务器连接了");
new Thread(){ //开启一个线程,让该线程与客户端交流。主线程就可以继续下一次循环
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
5-4相关类和API
UDP协议程序(了解)
用户数据报协议, 与tcp协议不同, UDP的连接是不可信的. 数据发送的成功与失败 与 数据报是无关的.
使用到两个类:
1.数据报套接字: DatagramSocket
用于发送 与 接收数据包的Socket
- 构造方法:
- DatagramSocket(int port);
- 参数: 端口号
- 常用方法:
- close() : 关闭套接字.
- send(DatagramPacket dp)
将一个数据包dp 发送出去
- receive(DatagramPacket dp)
接收一个数据包, 并存储到参数dp中.
2.数据包 DatagramPacket
用于发送或接收数据时, 盛放数据的对象!
- 构造方法:
1.用于发送数据时, 组装数据的 构造方法.
DatagramPacket(byte[] bytes,int startIndex,int len,InetAddress ip,int port);
参数1. 要发送的数据, 是字节数组的形式
参数2. 有效数据 在数组中的起始位置
参数3. 有效数据 在数组中的长度
参数4. 当前这个数据包, 准备发送到的IP地址, InetAddress 这个类的对象, 用于描述
IP .
得到InetAddress对象的方式:
- InetAddress ip = InetAddress.getByName("192.168.102.228");
参数5. 当前这个数据包, 准备发送到目标计算机的哪个端口号.
2.用于接收数据时, 存储数据的 构造方法.
创建的是 不包含数据的数据包, 用于在接收到数据后, 存储数据 !
DatagramPacket(byte[] bytes,int len)
参数1. 用于存储数据的 数组
参数2. 允许存储的最大长度
- 常用方法:
byte[] getData()
用于获取数据包中的有效字节数组
int getLength
用于获取数据包中的有效数据的长度.
InetAddress描述IP地址的类
InetAddress 这个类的对象, 用于描述IP .
得到InetAddress对象的方式:
InetAddress ip = InetAddress.getByName("192.168.102.228");
在UDP协议中. 通过数据包DatagramPacket的getAddress方法, 可以得到数据包来自哪个ip
在TCP协议中, 通过套接字Socket的getInetAddress方法, 可以得到套接字连接的ip地址.
- 常用方法:
1.String getHostAddress()
ip地址字符串
2.String getHostName()
计算机名称, 当名称无法获取时, 获取的为ip地址.
URL类(了解)
案例1:下载文件
public class ClientDemo {
public static void main(String[] args) throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("欢迎使用嘿嘿雷下载器");
System.out.println("请输入要下载的文件网址:");
String urlString = input.nextLine();
System.out.println("请输入要保存的文件名称:");
System.out.println("(文件默认下载位置:d盘download文件夹中)");
String fileString = input.nextLine();
//1. 确保文件夹存在
File dir = new File("d://download");
if(!dir.exists()) {
dir.mkdirs();
}
//2. 创建一个文件输出流, 用于输出数据
FileOutputStream fos = new FileOutputStream(new File(dir,fileString));
//3. 今天学习的新内容
//3.1 创建一个网址对象(统一资源定位符)
URL url = new URL(urlString);
//3.2 打开链接 , 并得到链接对象
URLConnection conn = url.openConnection();
//3.3 通过连接对象, 获取连接到的文件的输入流
InputStream is = conn.getInputStream();
//[3.4] 获取网址指向文件的 大小
long fileLength = conn.getContentLengthLong();
//3.5 循环读取 并写出到fos中
//用于存储 每次读取的数据
byte[] bytes = new byte[1024*1024];
//用于存储每次读取的数据长度
int len = -1;
//用于存储已读取的所有数据的长度
int count = 0;
while((len = is.read(bytes))!=-1) {
//将每次循环读取的bytes 写出到文件中
fos.write(bytes,0,len);
count+=len;
System.out.println("下载中:"+(count/(fileLength/100))+"%");
}
is.close();
System.out.println("文件下载完毕");
}
}
案例2:传输参数,并下载数据
static Scanner input = new Scanner(System.in);
public static void main(String[] args) throws Exception {
System.out.println("自动P图小程序:");
System.out.println("请选择菜单:");
int menu = menu();
System.out.println("请输入名字:");
String name = input.nextLine();
String name2 = URLEncoder.encode(name, "UTF-8");
//1. 得到网址, 这个网址指向的内容 是另一个图片的网址
String urlString = "http://itdage.cn/B/img?id="+menu+"&s1="+name2;
URL url = new URL(urlString);
//2. 打开链接
URLConnection conn = url.openConnection();
//3. 得到输入流
//3.1 因为我们这个网址的内容 只是一个图片的地址, 也就是一行文字, 所以我们将这个流转换为逐行读取流,
读取一行文本就可以了
InputStream is = conn.getInputStream();
//3.2 转换为字符流
InputStreamReader isr = new InputStreamReader(is);
//3.3 转换为逐行读取流
BufferedReader br = new BufferedReader(isr);
String imgUrlString = br.readLine();
System.out.println("图片已制作完成 , 地址:"+imgUrlString);
}
public static int menu(){
System.out.println("1. 捐*补助");//1
System.out.println("2. 登月插旗");//2 娃娃
System.out.println("3. 娃娃订单");//3 娃娃
System.out.println("4. 相思癌");//4相思癌
System.out.println("5. 孕检证明");
System.out.println("6. 玛莎拉蒂订单");//6.玛莎拉蒂订单
System.out.println("7. 马云湖畔大学");//
System.out.println("122. 男举牌 娶你");//
System.out.println("123. 女举牌 生猴子");//
String text = input.nextLine();
int m = -1;
try {
m = Integer.parseInt(text);
}catch(Exception e) {
}
if(m<1 || (m>7&&m<122) || m>123) {
return menu();
}
return m;
}
);
String name = input.nextLine();
String name2 = URLEncoder.encode(name, “UTF-8”);
//1. 得到网址, 这个网址指向的内容 是另一个图片的网址
String urlString = “http://itdage.cn/B/img?id=”+menu+"&s1="+name2;
URL url = new URL(urlString);
//2. 打开链接
URLConnection conn = url.openConnection();
//3. 得到输入流
//3.1 因为我们这个网址的内容 只是一个图片的地址, 也就是一行文字, 所以我们将这个流转换为逐行读取流,
读取一行文本就可以了
InputStream is = conn.getInputStream();
//3.2 转换为字符流
InputStreamReader isr = new InputStreamReader(is);
//3.3 转换为逐行读取流
BufferedReader br = new BufferedReader(isr);
String imgUrlString = br.readLine();
System.out.println(“图片已制作完成 , 地址:”+imgUrlString);
}
public static int menu(){
System.out.println(“1. 捐*补助”);//1
System.out.println(“2. 登月插旗”);//2 娃娃
System.out.println(“3. 娃娃订单”);//3 娃娃
System.out.println(“4. 相思癌”);//4相思癌
System.out.println(“5. 孕检证明”);
System.out.println(“6. 玛莎拉蒂订单”);//6.玛莎拉蒂订单
System.out.println(“7. 马云湖畔大学”);//
System.out.println(“122. 男举牌 娶你”);//
System.out.println(“123. 女举牌 生猴子”);//
String text = input.nextLine();
int m = -1;
try {
m = Integer.parseInt(text);
}catch(Exception e) {
}
if(m<1 || (m>7&&m<122) || m>123) {
return menu();
}
return m;
}