问题
多线程
1、谈谈对进程、线程、程序的理解
程序(program):为完成特定任务,用某种语言编写的一组指令的集合,一段静态的代码。
进程:正在运行的程序,进程作为资源分配的单位。
线程:程序内部的一条执行路径
2、代码完成继承Thread的方式创建分线程
3、代码完成实现Runnable接口的方法创建分线程
4、对比两种创建方式
* 多用接口实现Runnable
* 只能继承一个父类
*
* 联系
* Thread类本身实现了runnable接口
* 都要重写run方法,将要执行的逻辑声明在run方法中
5、Thread常用方法
* start()
* run()
* currentThread():静态方法,返回当前代码的线程
* getName():获取当前线程的名字
* setName():设置当前线程的名字
* yield():切换线程,释放当前cpu执行权
* join():在线程a中调用b的join()方法,线程a进入阻塞状态,直到线程B完全执行完之后,才结束阻塞状态
* sleep()
* isAlive();
*
*
* setPriority();
* getPriority();
6、线程的通信
wait() notify() notifyAll()
7、线程的生命周期
新键------》就绪-------》运行---------》阻塞 sleep() join(a); wait();
|-----------》死亡
8、线程同步
/**
* 出现重票和错票
* 线程不安全
* 出现的原因:
* 当某个线程尚未完成时,其他线程参与进来。
*
* 如何解决?
* 当一个线程,当操作ticket时,其他线程不能进来,直到操作完,其他线程才可以参与进来。
*
* 在java中,我们通过同步机制
* 1、同步代码快
* synchronized(同步监视器){
* 需要被同步的代码
* }
* 说明:操作共享数据的代码,即需要被同步的代买
* 共享数据:多个线程共同操作的数据
* 本问题中的ticket
* 同步监视器:俗称锁
* 任何一个对象都可以充当锁/多个线程都必须用同一把锁
* 2、同步方法
* 如果操作共享数据的代码完整的生命在一个方法中,我们不妨将此方法设置为同步
*
* 同步的方式
* 好处
* 1、解决了线程安全问题
* 坏处
* 1、变慢了,只有一个线程参与,其他线程等待,相当于单线程问题
*/
/**
* 使用同步方法解决实现Runnable接口的线程安全问题
* 关于同步方法的总结
* 1、同步方法仍然设计到同步监视器
* 2、非静态的同步方法,同步监视器是 this
* 静态的同步方法,同步监视器是:当前类本身
*/
9、单例模式之懒汉式
public static synchronized Bank getInstance()
{
//方式一:效率稍差
// synchronized(Bank.class){
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
//方式二:效率更高
if(instance == null){
synchronized(Bank.class){
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
10、死锁问题
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现思索后,不会出现异常提示,知识所有的线程都处于阻塞状态,无法继续
解决方法
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
11、Lock(锁)
class Window implements Runnable
{
private int ticket = 100;
//实例化 Reentrantlock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true)
{
try {
if (ticket > 0) {
//调用lock()
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + ticket);
ticket--;
} else
break;
}
finally {
//解锁
lock.unlock();
}
}
}
}
12synchronized 与 lock异同?
同
都是解决线程安全问题
不同
lock 是手动的锁定(lock),手动的解锁(unlock)
synchoronized,在执行完相应的代码逻辑以后自动的解锁。
选择
lock手动方式更加灵活
优先用Lock ------》同步代码块-------》同步方法
13、如何姐姐线程安全问题
lock 、 synchoronized
14、存钱
/**
* 银行有一个账户,有两个出乎分别向同一账户转入3000, 1000, 存3次,每次存完打印账户
*
*
* 分析
* 1、是否多线程 是
* 2、是否有共享数据 有,账户
* 3、是否有线程安全问题? 有 需要考虑如何解决线程安全问题,同步机制:有
*/
class Account{
private double balance;
public Account(double balance) {
this.balance = balance;
}
//存钱
public synchronized void deposit(double amt){
if(amt > 0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance += amt;
System.out.println(Thread.currentThread().getName() + "存钱成功,余额为" + balance);
}
}
}
class Customer extends Thread{
private Account account;
public Customer(Account acct){
this.account = acct;
}
@Override
public void run() {
for(int i = 0;i < 3; i++)
{
account.deposit(1000);
}
}
}
public class AccountTest
{
public static void main(String[] args) {
Account acct = new Account(0);
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
15、使用两个线程打印1-100。线程1,2交替打印
/**
* 设计到的三个方法
*
* wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
* notigy(): 一旦执行此方法,就会唤醒被wait的线
* notifyAll():唤醒所有
*
* 说明
* 1、wait notify notifyAll只能出现在同步方法,或者在同步代码块中(lock都不行)
* 2、省略this
* 3、三个方法的调用者必须使同步方法或者是同步方法中的同步监视器。
* 4、三个方法是定义在object类中的
* 否则会出现异常。
*/
class Number implements Runnable
{
private int number = 1;
@Override
public void run() {
while(true)
{
synchronized (this) {
//
notify();
if (number < 100) {
System.out.println(Thread.currentThread().getName() + " " + number);
number++;
try {
//使得调用wait()方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else
break;
}
}
}
}
public class CommunicationTest
{
public static void main(String[] args) {
Number number = new Number();
Thread thread = new Thread(number);
Thread thread2 = new Thread(number);
thread.start();
thread2.start();
}
}
16、面试题 sleep()和wait()方法异同
同:
一旦执行方法,都可以使当前线程进入阻塞状态
不同:
1、两个方法声明的位置不同:Thread类中声明sleep(), Object()类中声明wait();
2、调用范围不同:sleep()可以在任何需要的场景调用
wait()必须使用在同步代码块或者同步方法中;
3、关于是否释放同步监视器的问题:如果两个方法都使用在同步代码块或者同步方法中,
sleep方法不会释放同步监视器
wait()会释放同步监视器
17、生产者/消费者问题
生产者将产品交给店员,消费者取走产品,店员一次只能持有固定的产品,
package com.zhou.java2;
/**
* 生产者消费者问题
*
* 是否使多线程 是 生产和线程 消费者线程
* 是否有共享数据 是 产品
* 是否有线程冲突问题 是 产品数量
* 是否涉及到线程通信 是;
*
*/
class Clerk{
private int productCount = 0;
//生产产品
public synchronized void produceProduct() {
if(productCount < 20)
{
productCount++;
System.out.println(Thread.currentThread().getName() + "生产铲平" + productCount);
notify();
}
else
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumerProduct() {
if(productCount > 0)
{
System.out.println(Thread.currentThread().getName() + "消费铲平"+ productCount);
productCount--;
notify();
}
else
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ":开始生产");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ":开始消费");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumerProduct();
}
}
}
public class ProductTest
{
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
Consumer consumer = new Consumer(clerk);
producer.start();
consumer.start();
}
}
18、实现Callable接口
重写call方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果。
Future接口
可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等
FutureTask是Future接口的唯一实现类
如何理解比Runnable强大
call()可以抛异常
可以有返回值
callable可以支持泛型
package com.zhou.java2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* 船舰线程的方式三:实现Callable接口 jdk5.0新增
*
*/
//创建一个实现Callable的实现类
class NunThread implements Callable{
//将执行的逻辑放入call方法中
@Override
public Object call() throws Exception {
Integer sum = 0;
for(int i = 0; i < 100; i ++)
{
if(i % 2 == 0)
{
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//创建实现类对象
NunThread n = new NunThread();
//将实现类对象放入FutureTask中
FutureTask futureTask = new FutureTask(n);
//将FutureTaks的对象作为参数传递到Thread类的构造器中。
new Thread(futureTask).start();
try {
//get方法的返回值即为futureTask构造器参数Callable实现类call的返回值
Object o = futureTask.get();
System.out.println("总和为" + o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
19、使用线程池
思路:提前创建好多个线程,放入线程池中,使用的时候直接获取,使用完就放回。
好处:
- 提高相应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建线程的方式4:使用线程池
*/
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0; i <= 100; i++){
if(i % 2 == 0)
System.out.println(i);
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0; i <= 100; i++)
{
if(i % 2 == 0)
System.out.println(i);
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//提供指定线程数量的线程池
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//设置线程池的属性
executorService.setCorePoolSize(15);
executorService.setMaximumPoolSize(19);
//适合使用于Runnable
//执行指定的线程操作,需要提供实现Runnable接口
executorService.execute(new NumberThread());
executorService.execute(new NumberThread1());
//关闭线程池
executorService.shutdown();
//适合适用于Callable
//executorService.submit();
}
}
20、面试题 创建多线程有几种方式
4种 继承Thread 实现Runnable 实现Callable 线程池
21、释放锁操作
- 正常结束
- break return
- 异常
- wait()
22、不会释放
- sleep() yield()
- suspend()
Java常用类
23、String
String 是一个final类,代表不可变的字符序列
实现了Serializable接口:表示字符串是支持序列化的;
实现了Comparable接口:可以比较大小的
定义了final char[] value 用于存储字符串数据
-
当对字符串重新赋值时,需要重写指定的内存区域,不能对原有的直接修改
-
当现有的字符串进行操作时,也需要重新指定区域进行赋值
-
通过字面量的方式给一个字符串,此时的字符串的值声明在常量池中
补充
方法区(包含字符串常量池)栈和堆
public class stringTest{ public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; String s3 = "abc"; s3 += "def"; System.out.println(s2); System.out.println(s3); System.out.println(); String s4 = "abc"; String s5 = s4.replace('a', 'm'); System.out.println(s4); System.out.println(s5); }}
String 赋值
String str = "hello";String s1 = new String();String s2 ]= new String(String original);String s3 = new String(char[] a);String s4 = new String(char[] a, int startIndex, int count);
面试题
String str1 = “abc” 和 Strinh str2 = new String(“abc”); 有什么区别?
String s1 = "javaEE";
String s2 = "javaEE";
String s3 = new String("javeEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true
System.out.println(s3 == s4); //false;
Person p1 = new Person("Tom", 12);
Person p2 = new Person("Tom", 12);
System.out.println(p1.name == p2.name);//true
//结论
//常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
//只要其中有一个是变量,结果就在堆中
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
//有变量名参与,都在堆空间开辟,相当于new
String.out.println(s3 == s4); //true
String.out.println(s3 == s5); //false
String.out.println(s3 == s6); //false
String.out.println(s3 == s7); //false
String.out.println(s5 == s6); //false
String.out.println(s5 == s7); //false
String.out.println(s6 == s7); //false
String s8 = s5.intern();
System.out.println(s3 == s8);//true
//常量跟常量的拼接在常量池
//final 是常量
final String s9 = "javaEE";
String s10 = s9 + "hapoop";
System.out.println(s3 == s5); //true
面试题
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char ch[])
{
//不可变,不影响
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args)
{
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.println(ex.str); //good
System.out.pribtln(ex.ch); //best
}
24、jvm Heap堆
一个jvm实例只存在一个堆内存,堆内存的大小
- 新生区
- 养老区
- 永久区(虽然jvm规范将方法区描述为堆的一个逻辑部分,但它还有一个别名叫非堆,目的就是要和堆分开)
字符串常量池在jdk1.6在方法区 1.7在堆中 1.8在方法区
25、String常用方法
String s1 = "helloworld";
System.out.println(s1.length());
System.out.println(s1.charAt(0));
System.out.println(s1.isEmpty());
System.out.println(s1.toLowerCase());
System.out.println(s1.toUpperCase());
String s2 = s1.trim(); //删除两端空格
s1.equalsIgnoreCase(s2); //忽略大小写
String s4 = s1.concat("def");//连接
String s5 = s1.substring(1);
endsWith(String suffix); //判断结尾
startWith(String prefix);//判断开头
startWith(String prefix, int toffset);//判断开头,指定位置
boolean contains(CharSequence s); //是否半酣此字符串
int indexOf(String str); //是否半酣,并返回索引
int lastIndexOf(String str);
String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);
String replaceAll(String regex, String replacement); //正则表达式
String replaceFirst(String regex, String replacement);
String[] split(String regex);
26、String转换问题
String str1 = "123";// int num = (int) str1;错误int num = Integer.parseInt(str1);//基本数据类型转换成StringString str2 = String.valueOf(num);String str3 = num + "";//String 转换 char[]String str4 = "abc";char[] charArray = str4.toCharArray();String str4 = new String(charArray);
String str1 = "abc123";char[] charArray = str1.toCharArray();reverse(charArray, 0, 2);str1 = new String(charArray);
- String 与byte[] 之间的转化吧
String str1 = "abc123";
//String 转 byte[] getBytes();
byte[] b = str1.getBytes();
System.out.println(Arrays.toString(b));
String str2 = "abc123中国";
byte[] bytes = str2.getBytes();//使用默认的字符集进行转化
System.out.println(Arrays.toSstring(bytes));//中文对应三个
byte[] bytes2 = str2.getBytes("gbk");//使用gbk编码格式
System.out.println(Arrays.toString(bytes2));//中文对应gbk两个
//逆过程 解码
String str3 = new String(bytes);
System.out.println(str3); //没毛病
String str4 = new String(bytes2);
System.out.println(str4); //出现乱码,解码编码格式不一样
String str5 = new String(bytes2, "gbk");
System.out.println(str5); //没有出现乱码,没毛病
27、编码解码
编码:字符串----》字节
解码:字节-----》字符串
28、常见算法题目
29、StringBuffer和StringBuilder
String StringBuffer StringBuilder异同
String:不可变的字符序列, 底层都用char[]数组存储
StringBuffer:可变的字符序列;效率低,线程安全
StringBuilder:可变的字符序列 1.5 线程不安全,效率高
如何选择?
是否是多线程问题 多线程用StringBuffer
StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];sb1.append('a');//value[0] = 'a';sb1.append('b'); //value[1] = 'b';StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
问题1、
sb1.length() = 2;
sb2.length9) = 3;
问题2
扩容问题:如果要添加的数据底层数组盛不下了,那就扩容数组
开发中建议使用StringBuffer(int capacity) 构造器,避免扩容,复制
30、StringBuffer方法
StringBuffer s1 = new StringBuffer("abc");s1.append(1);s1.append('1');System.out.println(s1);s1.delete(2, 4);System.out.println(s1); //ab1s1.replace(2, 4, "hello");s1.insert(2, false)
总结:
增:append()
删:delete(); setChart
改:replace
查:charAt(int n);
插:insert(int offset, xxx)
长度:length();
遍历:for() + charAt()//toString();
31、String StringBuffer StringBuilder效率对比
StringBuilder快于StringBuffer.>>>>>>>>>>String
32、jdk8之前日期时间API
public class DateTimeTest{ /** * java.util.Date类 * |----java.sql.Date * 1、两个构造器的使用 * * 2、两个方法的使用 * toString():显示当前年月日时分秒 * getTime():获取当前Date对象对应的毫秒数 * * 3、java.sql.Date对应数据库中的日期类型变量 * 》如何实例化 * 》sql.Date ----> util.Date 直接赋值就行 */ @Test public void test2() { //创建了一个当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString()); System.out.println(date1.getTime()); //构造器二:创建指定毫秒数的date对象 Date date2 = new Date(date1.getTime()); System.out.println(date2.toString()); } //System类中的currentTimeMillis(); @Test public void test1() { long time = System.currentTimeMillis(); //1970 1 1 到现在的毫秒数 //称为时间戳 System.out.println(time); }}
SimpleDateFormate
Date类的Api不宜与国际化,大部分被废弃了
public void testSimpleDateFormat() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat(); Date date = new Date(); System.out.println(date); //格式转化 String format = sdf.format(date); System.out.println(format); //格式化的逆过程 字符串----》日期 String str = "2021-1-1 上午11:43"; Date date1 = sdf.parse(str); System.out.println(date1); //指定方式进行格式化 new SimpleDateFormat("yyyy-MM-dd"); }
Calendar 日历类
public void testCalender(){ //实例化 //方式一:创建其子类的对象 //方式二:调用其静态方法 Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass()); //2、常用方法 //get //当前这个日期是这个月的第几天 //月份从0开始 int days = calendar.get(Calendar.DAY_OF_MONTH); //set //修改日期 calendar.set(Calendar.DAY_OF_MONTH, 22); //add()、 calendar.add(Calendar.DAY_OF_MONTH, 11); //getTime() Date time = calendar.getTime(); //setTime() calendar.setTime(time);}
jdk8中新日期详见40
33、如何理解String的不可变性
每次改变都会新造一个String
34、模拟一个trim方法,去除字符串两端的空格
35、将一个字符串进行反转,将字符串指定部分进行反转
public String reverse(String str, int startIndex, int endIndex){ if(str == null || str.length() == 0) return str; char[] arr = str.toCharArray(); for(int x = startIndex, y = endIndex;x < y;x++,y--) { char temp = arr[x]; arr[x] = arr[y]; arr[y] = temp; } return new String(arr);}//方式2:使用String拼接操作public String revese1(String str, int startIndex, int endIndex){ String reveseStr = str.substring(0, startIndex); for(int i = endIndex; i >= startIndex; i--) { reveseStr += str.charAt(i); } reveseStr += str.substring(endIndex + 1);}
36、获取一个字符串,在另一个字符串中出现的次数
public int getCount(String mainStr, String subString)
{
int mainlength = mainStr.length();
int subLength = subString.length();
if(mainlength >= subLength)
{
int count = 0;
int index = 0;
while(true)
{
if((index = mainStr.indexOf(subString)) != -1)
{
count ++;
mainStr = mainStr.substring(index + 1);
}
else
break;
}
return count;
}
else
return 0;
}
public int getCount(String mainStr, String subString)
{
int mainlength = mainStr.length();
int subLength = subString.length();
if(mainlength >= subLength)
{
int count = 0;
int index = 0;
while((index = mainStr.indexOf(subString, index + 1)) != -1)
{
count ++;
index ++;
}
return count;
}
else
return 0;
}
37、获取两个字符串中的最大相同子串
public String get(String str1, String str2){ String maxStr = str1.length() >= str2.length() ? str1 : str2; String minStr = str1.length() < str2.length() ? str1 : str2; int length = minStr.length(); for(int i = 0; i < length; i++) { for(int x = 0, y = length - i;y <= length;x++,y++) { String subStr = minStr.substring(x, y); if(maxStr.contains(subStr)){ return subStr; } } }}
39、代码阅读
public void testStringBuffer(){ String str = null; StringBuffer sb = new StringBuffer(); sb.append(str);// System.out.println(sb.length());4 System.out.println(sb); //"null" StringBuffer sb1 = new StringBuffer(str); //抛异常NullPointException System.out.println(sb1);}
40、jdk8中新日期时间API
Calender比Date有不少改进 但是有很多问题
- 可变性:像日期和时间这样的类应该是不可变的
- 偏移性:Date的开始日期,年份是从1900开始,月份是从0开始的
- 格式化只对Date有用,Calendar则不行
此外不是线程安全的,不能处理闰秒等;
引入java.time包
包含本地日期(LocalDate) 本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类
Date类中也加入了方法转换
public void LocalDateTest(){
//LocalDate LocalTime, LocalDateTime
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
//使用频率高
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//设置指定的年月日没有偏移量
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 34);
System.out.println(localDateTime1);
//都有这个方法 getxxx
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getDayOfYear());
System.out.println(localDateTime.getMonth());
System.out.println(localDateTime.getMonthValue());
System.out.println(localDateTime.getMinute());
// 都有withxxx
LocalDate localDate1 = localDate.withDayOfMonth(22);
System.out.println(localDate1);
System.out.println(localDate); //不可变性,本身不该,都是这样的
//plus 不可变性
LocalDateTime localDateTime2 = localDateTime.plusMonths(3);
//minus......
}
41、瞬时Instant
时间线上的一个瞬间点。这可能被用来记录应用程序中的时间戳。
从1970.1.1开始。
类似于Date
@Test
public void test2()
{
Instant instant = Instant.now();
//东加西减,本初子午线时间
System.out.println(instant);
//设置到东八区的时间
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//计算毫秒数多少
long l = instant.toEpochMilli();//获取到现在的毫秒数
System.out.println(l);
//可以通过毫秒数设置时间
Instant instant1 = Instant.ofEpochMilli(111111L);
System.out.println(instant1);
}
42、DateTimeFormatter:格式化或解析日期
类似于SimpleDateFormat
public void test3()
{
//方式一:预定义标准格式;如下
DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化
String format = isoLocalDateTime.format(LocalDateTime.now());
System.out.println(format);
//解析
TemporalAccessor parse = isoLocalDateTime.parse(format);
System.out.println(parse);
//方式二:本地化方式,ofLocalLizedDateTime();
//参数 FormatStyle.LONG FormatStyle.MEDIE / FormatStyle.SHORT
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
String format1 = dateTimeFormatter.format(LocalDateTime.now());
System.out.println(format1);
//ofLocalLizedDate()多了个参数 FormatStyle.FULL;
//方式三:自定义的格式。如:“yyyy-MM-dd hh-mm-ss";
//重点
//重点
//重点
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String format2 = dateTimeFormatter1.format(LocalDateTime.now());
System.out.println(format2);
}
43、Comparable接口的使用
//Comparable使用举例String[] arr = new String[]{"AA", "CC", "MM"};Arrays.sort(arr);System.out.println(arr);/*重写规则当前this大于obj 返回正整数 等于 0 小于 负数*/
- 自定义类实现Comparable接口
public class Goods implements Conparable{ private String name; private int a; //加个从低到高排序 @Override public void compareTo(Object o) { if(o instanceof Goods){ Goods goods = (Goods) o; if(this.price > goods.price){ return 1; } else if(this.price < goods.price) return -1; else return 0; } throw new Exception(); }}public void test2();{ Goods[] arr = new Goods[4]; arr[0] = new Good("lianxiangMous", 30); arr[0] = new Good("xiangMous", 30); arr[0] = new Good("lianMous", 30); arr[0] = new Good("lianxiang", 30);}
44、定制排序:Comparator
//背景
//当元素类型没有实现Comparable接口
//实现了了Comparable接口,但不适合
//可以考虑使用定制的Comparator
//重写comparator() 有连个参数
public void test()
{
String[] arr = new String("AAA", "ccc");
Arrays.sort(arr, new Comparator(){
@Override
public int compare(Object o1, Object o2)
{
if(o1 instanceof String &&
o2 instanceof String)
{
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
throw new RuntimeException("输入类型不一致");
}
})
}
public void test4()
{
Goods[] arr = new Goods[5];
.....;
Arrays.sort(arr, new Comparator(){
@Override
public int compare(Object o1, Object o2)
{
if(o1 instanceof Goods && o2 instanceof Goods)
{
Goods g1 = (Goods)o1;
Goods g2 = (Goods) o2;
if(g1.getName().equlas(g2.getName()))
{
return -Double.compare(g1.getPrice, g2.getPrice);
}
else
return g1.getName().compareTo(g2.getName());
}
return 0
}
})
}
对比
- Comparable接口的方式一旦确定,在任何位置都能比较大小
- Comparator属于临时比较
45、System类
//currenTimeMillis();
//exit();退出程序 0,表示正常退出,, 非0表示异常退出
//gc();垃圾回收器
//getProperty(String key);获取属性
/* 常用的
"java.version" 版本
“Java.home" 文件路径
”os.name" 系统名称
”os.version" 系统版本
“user.name" 用户名称
*/
46、Math 数学类
- abs 绝对值
- acos, asin, atan, cos, sin, tan 单间函数
- sqrt 开放
- pow(double a, double b) 次幂
- log 自然底数
- exp e为底自然数
- max
- min
- random
- long round()
- toDegrees 弧度—》角度
- toRadians() 角度—》弧度
47、BigInteger
Math包下的BigInteger 可以表示不可变的任意精度的整数
Integer 2 31 - 1
Long 2 63-1
48、BigDecimal类
浮点型,高精度。任意精度
BigInteger bi = new BigInteger("123232131");
BigDecimal bd = new BigDecimal("12323213.1231");
BigDecimal bd2 = new BigDecimal("11");
bd.divide(bd2, ROUND_HALF_UP);
bd.divide(bd2, 25, ROUND_HALF_UP);
49、2017-08-16转化为java.sql.Date类对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.parse("2017-08-16");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
50、解释何为编码?解码?何为日期时间的格式化?
编码:字符串-》字节数据
解码:字节-》字符串
格式化:日期-》字符串
解析:字符串-》日期
51、JDK8 之前和jdk8中日期、时间相关的类分别有那些
java.util.Date 和 java.sql.Date -> Instant
SimpleDateFormat -->>>> DateTimeFormatter
Calendar ------> LocalDate LocalTime LocalDateTime
51、枚举类
/** * 一、枚举类的使用 * 注意:若枚举类只有一个对象,则可以作为一种单例模式的实现方式 * * 二、如何定义枚举类 * 方式一: * jak5.0之前自定义枚举类 * 方式二: * 之后可以用enum关键字定义枚举类型 * * 三、Enum类中的常用方法 * values(); * valueof(); * * 四、自定义枚举类实现接口 * 可以让枚举类分别实现接口中的抽象方法 */public class EnumTest { public static void main(String[] args) { Season1 spring = Season1.SPRING; //values(); Season1[] values = Season1.values(); //valueof(); Season1 spring1 = Season1.valueOf("SPRING"); }}
public class EnumTest { public static void main(String[] args) { Season spring = Season.SPRING; }}class Season{ //提供当前枚举类的多个对象 public static final Season SPRING = new Season("春天", "春"); public static final Season SUMMER = new Season("春天", "春"); public static final Season AUTUMN = new Season("春天", "春"); public static final Season WINTER = new Season("春天", "春"); //声明Season对象的属性 //需要private final 修饰 private final String seasonName; private final String seasonDesc; //1、私有化类的构造器,并给对象书香赋值 private Season(String seasonDesc, String seasonName){ this.seasonName = seasonName; this.seasonDesc = seasonDesc; } public String getSeasonName(){ return seasonName; } public String getSeasonDesc(){ return seasonDesc; }}
//之后可以用enum关键字定义枚举类型
//使用enum关键字枚举类
enum Season1{
SPRING("春天", "春"),
SUMMER("夏天", "下");
private final String seasonName;
private final String seasonDesc;
private Season1(String seasonName, String seasonDesc)
{
this.seasonDesc = seasonDesc;
this.seasonName = seasonName;
}
}
52、注解 Annotation
概述
jdk5.0新增元数据即Annotation
特殊标记,运行时被读取,并执行相应的处理,通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代马分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署
javase中注解比较简单,在javaee/android中注解占据了更重要的角色,。配置文件可以用注解进行替换。
JPA是基于注解的,Spring2.5以上都是基于注解的
框架 = 注解 + 反射+ 设计模式。
53、如何自定义注解
public @interface MyAnnotation {
String value() default "hello";
}
54、基本注解
@Override
限定重写
@Deprecated
过时了,不建议使用
@SuppressWarings
抑制编译器警告
55、元注解: 修饰其他注解的注解
一般用上Retention Target
- Retention
指定注解的声明周期
SOURCE 通过编译的时候discarded
CLASS (默认行为) 将被保留在class文件中,但不会在run的时候不会加载
RUNTIME 保留在class文件中,run时加到内存中,只有这个才能通过反射获取。
- Target
指定被修饰注解能修饰那些数据
TYPE: class interface
FIELD
METHOD…
没写在哪里都能用
- Documeneted
javadoc一般不包含注解信息
如果想被包含,则在注解上加这个注解
- Inherited
继承性,如果有类用了该注解,子类就自动加上该注解
通过反射来获取注解信息
Class clazz = Student.class;
Annotation[] annotations = clazz.getAnnotations();
System.out.println(annotations);
56、元数据: 对现有数据修饰的数据
String name = “atguigu”;
元数据 数据
jdk8 注解新特性
- 可重复注解
可以重复定义多个
//以前这样public @interface MyAnnotations{ MyAnnotation[] value();}@MyAnnotations({@MyAnnotation(value = "hi")...})//jdk8之后@Repeatable(MyAnnotations.class)public @interface MyAnnotation{ MyAnnotation[] value();}
- 类型注解
@Target({TYPE, TYPE_PARAMETER, TYPE_USE})public @interface MyAnnotation{ MyAnnotation[] value();}//可以注解泛型class Gemeric<@MyAnnotation T>{ }
JAVA集合
57、集合框架
/------Collection接口, 单列集合,用来存储一个一个对象
/--------List接口 元素有序、可重复 ”动态数组"
/--------Set接口 元素无需、不可重复 “高中集合”
/------Map接口:双列集合,一堆一对数据
/--------HashMap, LinkedHashMap, TreeMap
58、Collection接口中的API
@Test
public void test1()
{
Collection coll = new ArrayList();
//add()
coll.add("AA");
coll.add("bb");
coll.add("123");
//size()
System.out.println(coll.size());
//addAll()
Collection coll2 = new ArrayList();
coll2.add("CC");
coll.addAll(coll2);
//clear
coll.clear();
//isEmpty();
System.out.println(coll.isEmpty());
coll.contains(new String("aa"));
coll.remove(123);
Collection coll1 = Arrays.asList(123, 345);
coll.removeAll(coll1);
//求交集,结果给coll
coll.retainAll(coll1);
//判断当前集合和形参是否一样 有序
System.out.println(coll.equals(collq));
//hashCode();返回当前对象的hash值
coll.hashcode();
//集合转换为数组,toArray();
Objecep[] arr = coll.toArray();
//转化成Collection
Arrays.asList(new String[]{"ji", " aefea"});
List arr1 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr1);
//iterator():返回Iterator接口的实例,用于遍历集合元素
}
59、集合元素的遍历 使用Iterator
//Iterator接口 迭代器接口//遍历用接口Collection coll = new ArrayList();coll.add(1223);coll.add(466);Iterator iterator = coll.iterator();System.out.println(iterator.next());//没有元素使用next() 报异常NoSychElementException();while(iterator.hasNext()){ Object obj = iterator.next(); if("Tom".equals(obj)){ iterator.remove(); }}//remove方法移除
60、List接口概述 (动态数组)
ArrayList LinkedList Vector异同
同
- 都实现了List接口,存储数据的特点相同:存储有序的数据
不同
- ArrayList:作为List接口的主要实现类,线程不安全的,效率高,底层使用Object类型数组存储
- LinkedList: 底层使用双向链表,对于频繁插入删除操作,使用此类效率高
- Vector:作为List接口的古老实现类,线程安全的,效率低,底层使用Object类型数组存储
61、ArrayList底层源码
JDK7情况下
ArrayList list = new ArrayList();
底层是容量为10的Object[]; 默认扩容为原容量的1.5倍
结论:建议使用代餐的构造器 new ArrayList(100);
JDK8中的变化
底层初始化为{} 并没有创建
第一次add 创建10 的容量
小结
JDK8中内存节省了, jdk7中是饿汉式 jdk8中类似于懒汉式
‘
62、LinkedList源码分析
内部声明了 node类型的 first last 属性
Node定义为:双向链表节点
63、List接口常用操作
ArrayList list = new ArrayList();
list.add(123);
list.add(2321);
//在第一个位置插入
list.add(1, 12321);
//addAll();
List list1 = Arrays.asList(1,3,4);
list.addAll(list1);
System.out.println(list.size());
//get方法
System.out.println(list.get(0));
//indexOf();首次出现的索引位置
System.out.println(list.indexOf(123));
//lastIndexOf();找不到返回值是-1;
//remove(int i);按照索引删除
//set(int , Object);
//sublist;
64、Set接口:无序不可重复的
set没有额外定义新方法,使用的都是Collection中定义过的
- HashSet:作为Set接口的主要实现类;线程不安全
-
- LinkedHashSet: HashSet子类 ,指针,看似有序,按照添加顺序来遍历
- TreeSet: 使用红黑树存的,同一个类new的对象,可以排序,可以按照添加对象的指定属性进行排序
public static void main(String[] args) {
//1、无序性 != 随机性
/*
hashset底层是用数组存的
存的值并非按照数组索引顺序添加,而是根据哈希值决定的
*/
//2、不可重复性
/*
保证添加的元素按照equals()判断时,不能返回true;即相同的元素只能添加一个
*/
Set set = new HashSet();
set.add(1324);
set.add(342);
set.add("aa");
Iterator iterator = set.iterator();
}
65、Map接口,双列数据,存储key-value对的数据
HashTable----》Properties; 古老的实现类,线程安全,效率低
HashMap------》LinkedHashMap: 主要实现类,线程不安全,效率高,可以存储null的Key和value
TreeMap-----》实现key-value进行排序,按照Key来排的
- 底层使用红黑树
jdk7 数组+链表
jdk8 数组+链表+红黑树
Properties:常用来处理配置文件,key和value都是String类型
public void Maptest{ Map map = new HashMap(); //map = new HashTable(); map.put(null, null);}
66、HashMap底层实现原理
67、HashMap和Hashtable的异同
同:都是一对对数据
异
- 主要实现类,线程不安全,效率高,可以存储null的Key和value
- 古老的实现类,线程安全,效率低
68、HashMap底层实现原理;(jdk7)
HashMap map = new HashMap();//实例化以后,底层创建了一个长度为16的entry数组map.put(key1,value1);//前面已经put了//首先计算key1的hash值//如果此位置数据为空,此时添加成功,//如果不为空,意味着此位置存在一个或者多个数据,比较当前key1和已经存在的hash值//如果key1哈希值与已经存在的数据hash值都不相同,此时添加陈坤//如果key1哈希值和已经存在的某一个hash值相同,调用equals方法,//在不断的添加过程中,会涉及到扩容问题,默认扩容为原来容量的2倍,并将原有的数据赋值过来
jdk8区别
- new HashMap() 没有创建一个长度为16的数组
- jdk8 底层数组是node[]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk7底层结构只有数组加链表, jdk8数组+链表+红黑树
- 当数组的某一个索引位置上的元素以链表形式的存在的数据个数>8且当前数组的长度>64,此时此索引位置上的所有数据改为使用红黑树存储
69、Map常用方法
HashMap map = new HashMap<String, Integer>();
map.put("aa", 213);
map.put("bb", 343);
Map map1 = new HashMap();
map1.put("cc", 333);
map1.putAll(map);
map.remove("cc");
map.clear();
System.out.println(map);
map.get("aa");
boolean aa = map.containsKey("aa");
boolean b = map.containsValue(123);
boolean empty = map.isEmpty();
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
Collection collection = map.values();
for(Object obj : collection)
{
System.out.println(obj);
}
70、properties
Hashtable子类,该对象是处理配置文件
key和value是string
public static void main(String[] args) throws IOException {
Properties pros = new Properties();
FileInputStream fis = new FileInputStream("a.pro");
pros.load(fis);
String name = pros.getProperty("name");
String password = pros.getProperty("password");
}
71、Collections工具类的使用
public void test(){ List list = new ArrayList();; list.add(123); list.add(345); list.add(-12); Collections.reverse(list); System.out.println(list); Collections.shuffle(list); System.out.println(list); Collections.sort(list); //Collections.max() int frequency = Collections.frequency(list, 123); System.out.println(frequency); List dest = Arrays.asList(new Object[list.size()]); Collections.copy(dest, list); List list1 = Collections.synchronizedList(list);}
72、泛型:为什么要用泛型
把元素的类型设计成参数,参数就是泛型
public void test10(){ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); list.add(89);}
73、自定义泛型结构
泛型类
public class Order<T>{
String orderName;
int orderId;
T orderT;
public Order(){
//错误,编译不通过
T[] arr = new T[10];
//编译通过
T[] arr = (T[]) new Object[10];
}
}
public class SubOrder extends Order<String>{
}
public class SubOrder2<E> extends Order<E>{
}
74、泛型方法
public<T> List<T> copyFromArrayToList(T[] arr){
ArrayList<T> list = new ArrayList<>();
for(E e: arr)
list.add(e);
return list;
}
使用List<?>就不能向里面加父类了
例外
除了添加null之外
75、IO流:File类基本使用
/** * 1、如何创建File类的实例 * File(String filepath); * File(String parentpath, String childpath); * File(File parentFile, String childpath); * * 2、 * 相对路径 * 绝对路径 * * 3、路径分割服 * windows下 \\ * unix:/ */@Test public void Test() throws IOException { File file = new File("hello.txt"); //getAbsolutePath();获取绝对路径 System.out.println(file.getAbsolutePath()); //获取路径 System.out.println(file.getPath()); //获取名字 System.out.println(file.getName()); //获取parent System.out.println(file.getParent()); //获取长度 System.out.println(file.length()); //最近修改时间, long毫秒数 System.out.println(file.lastModified()); //list(),下一层目录内文件的名字 String[] list = file.list(); //listFiles(),直接输出文件 File[] files = file.listFiles(); //重命名, 返回值看看是不是改名成功,并且改变路径 //file 一定要存在 //且file2不能存在 File file2 = new File("hi.txt"); boolean b = file.renameTo(file2); //判断功能 //判断是否为文件目录 file.isDirectory(); //是否文件 file.isFile(); //是否存在 file.exists(); //是否可读可写 file.canRead(); file.canWrite(); //是否隐藏 file.isHidden(); //创建硬盘中对应的文件或者文件目录 //不走回收站 File file3 = new File("hi.txt"); if(!file3.exists()) { boolean newFile = file3.createNewFile(); } File file4 = new File("d:/fda/fdha"); file4.mkdir(); file4.mkdirs(); file4.delete(); //旗下不能有子目录或者子文件 }
76、如何遍历Map的key集, value集, key-value集, 使用上泛型
Map<String, Integer> map = new HashMap<>();Set<String> keySet = map.keySet();for(String key : keySet){ System.out.println(key);}Collection<Integer> values = map.values();Iterator<Integer> iterator = values.iterator();while(iterator.hasNext()){ System.out.println(iterator.next());}Set<Map.Entry<String, Integer>> entry = map.entrySet(); Iterator<Map.Entry<String,Integer>> iterator = enetry.iterator();while(iterator.haxNext()){ Map.Entry<String, Integer> entry = iterator.next(); System.out.println(entry.getKey() + entry.getValue());}
77、IO流原理及流的分类
I = input O = output
处理设备之间的数据传输
数据单位不同:字节流(8bit) 字符流(16bit);
流的角色不同:节点流, 处理流
抽象基类 InputStream OutputStream Reader Writer
节点流(文件流) FileInputStream FileOutputStream FileREader FileWriter
缓冲流 Buffered Buffered Buffered Buffered
public void test()
{
FileReader fr = null;
try {
//1、实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");//相较于当前Module
//2、提供具体的流
fr = new FileReader(file);
//3、数据的读入
//read():返回读入的字符,如果达到文件末尾,就返回-1;
// int read = fr.read();
// while(read != -1)
// {
// System.out.println((char)read);
// read = fr.read();
// }
//语法上的修改
int data;
while ((data = fr.read()) != -1) {
System.out.println(data);
}
}
catch (Exception e)
{
System.out.println(e);
}
finally {
//流的关闭操作
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//流的关闭操作
}
public void etst()
{
FileReader fr = null;
FileWriter fw = null;
try{
fr = new File("")
}
}
FileInputStream不能处理txt byte 复制可以
FileReader不能处理图片 char
78、缓冲流BufferedInputStream
为了提高读写效率
public void BufferedStreamTest{ //1、造文件 File srcFile = new File("aa.jpg"); File destFile = new File("aa1.jpg"); //2、造流 节点流 FileInputStream fis = new FileInputStream(srcFile); FileOutputStream fos = new FileOutputStream(destFile); //3、造缓冲流 BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); //4、复制细节 byte[] buffer = new byte[5]; int len; while((len = bis.read(buffer) != -1)) { bos.write(buffer, 0, len); } //5、关闭资源 //先关外层的,再关内层的 bos.close(); bis.close(); //在关闭外层流的同时,内层流也会自动进行关闭,可以省略 //fos.close(); //fis.close();}
BufferedReader
public void test(){ //创建文件相应的流 BufferedReader br = new BufferedReader(new FileReader(new File("dbcp.txt"))); BufferedWriter bw = new BufferedWriter(new FileWriter(new File("dbcp2.ttxt"))); //读写操作 char[] cbuf = new char[1024]; int len; while((len = br.read(cbuf)) != -1) { bw.write(cbuf, 0, len); } //方式二 String data; while((data = br.readLine()) != null) { bw.write(data + "\n");//data中不包含换行符 } //关闭资源 bw.close(); br.close();}
79、图片的加密
public void test1(){ FileInputStream fis = new FileInputStream(new File("aa.jpg")); FileOUtputStream fos = new FileOutputStream(new File("aa1.jpg")); byte[] buffer = new byte[20]; int len; while((len = fis.read(buffer)) != -1) { for(int i = 0; i< len; i++) { buffer[i] = (byte) (buffer[i] ^ 5); } fos.write(buffer, 0, len); } fos.close(); fis.close();}
80、转换流 InputStreamReader//OutputSteamWriter
转换提供了字节流和字符流之间的转换
解码:字节、字节数组------》字符数组、字符串
编码:字符数组、字符串-----》字节、字节数组
- 字符集
public void test(){ FileInputStream fis = new FileInputStream("aa.txt"); InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1) { String str = new String(cbuf); System.out.print(str): } isr.close();}
81、常用字符集
- ASCII:美国标准信息交换码 用一个字节的8位表示
- ISO8859-1:拉丁码表,欧洲码表, 用一个字节的8位表示
- GB2312:中国的中文编码表。最多两个字节编码所有字符,看首位是0是1解决是否是两个字节
- GBK:中国的中文编码的升级,融合了更多的中文文字符号,最多两个字节编码
- Unicode:国际标准版,融合了目前人类使用的所有字符,为每个字符分派唯一的字符码。都用两个字节存
- utf-8边长的编码方式,是对Unicode的落地实现
82、标准输入输出流/打印流/数据流
//标准的输入、输出流//System.in:标准输入流,默认从键盘输入//System.out:标准输出流,默认从控制台输出//setIn() 重定向//setOut() 重定向 //练习//从键盘输入字符串,要求将读取到的整行字符串转换成大写输出,然后继续进行输入操作//直到输入e退处程序public void test1(){ //方法一;用Scanner方法,调用next()返回一个字符串 //方法二:使用System.in实现。 InputStreamReader isr = new InputStreamReader(Sysstem.in); BufferedReader br = new BufferedReader(isr); while(true) { String data = br.readLine(); if(data.equalsIgoreCase("e") || data.eaualsIgnoreCase("exit")) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase): } br.close();}
//打印流,只有输出
//PrintStream PrintWriter
//提供了一系列冲裁的print() 和 println()方法
public void test()
{
PrintStream ps= null;
try{
FileOutputStram fos = new FileOutputStream(new File("aaa.txt"));
ps = new PrintStream(fos, true);
if(ps != null){
System.setOut(ps);
}
for(int i = 0; i <= 255; i++)
{
System.out.println((char) i );
if( i % 50 == 0){
System.out.println();
}
}
}
}
//数据流 操作基本数据类型和String
//DataInputStream DataOutputStream
//用于读取或写出基本数据类型的数据
public void test3(){
DataOutputStream dos = new DataOutputStream(new FileOutpetStream("data.txt"));
dos.writeUTF("姓名");
dos.writeInt(23);
dos.writeBoolean(true);
dos.close();
}
//读取要根据写入的顺序
public void read(){
DataInputStream in = new DataInputStream(new FileInpputStream("data.txt"));
in.readUTF();
in.readInt();
in.readBoolean();
}
83、对象流
ObjectInpuStream ObjectOutputStream
- 序列化:用ObjectOutputStream类保存基本数据类型或者对象的机制
- 反序列化:用ObjectInputStream类读取基本类型数据或者对象的机制
不能序列化static和transient修饰成员变量
对象序列化机制允许把内存中的java对象转换成平台无关的二进制流,从而允许把这些二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的JAVA对象
序列化的好处再余可将任何实现了Serializable接口的对象转化成字节流
对象流的使用 ObjectInpuStream ObjectOutputStream
//序列化过程public void OutputStream(){ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("aa.dat"))); oos.writeObject(new String("aaa"))); oos.writeObject(new Person("张三", 21)); oos.close;}//反序列化过程public void inpoutStream(){ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("aa.dat")); Object obj = ois.readObject(); Object obj = ois.readObject(); String str = (String) obj; ois.close;}//Person需要满足如下要求方可序列化//1、需要实现接口 Serializable //标识接口//2、需要提供一个序列化版本号serialVersionUID//3、除了当前Person类可序列化要实现Serializable接口之外,还必须保证其内部其他属性也是可序列化的。//4、不能序列化static和transient修饰成员变量@Datapublic class Person implements Serializable{ //序列版本号,自定义异常类都要序列化版本号。 public static final long serialVersionUID = 44444L; private String name; private int age;}
如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID会改变,则后续无法还原
84、随机存取文件流 RandomAccessFile
既可以作为输入流,也可以作为输出流。直接继承Object
实现接口 DataInput, DataOutput
重要性差一些
如果RandomAccessFile作为输出流时, 写出到的文件不存在,则创建,
如果写出到的文件存在,则会对原有文件进行覆盖,默认从头覆盖
@Test
public void test1()
{
//第二个参数可以选择"r" "rw"
//"rwd" 同步文件内容的更新
//"rws" 同步文件内容和元数据的更新
RandomAccessFile raf1 = new RandomAccessFile(new File("aa.txt"), "r");
RandomAccessFile raf2 = new RandomAccessFile(new File("aa.txt"), "rw");
byte[] buffer = new byte[1024];
int len;
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer, 0, len);
}
raf1.close();
raf2.close();
}
@Test
public void test2(){
//对文件内哦让那个的覆盖
RandomAccessFile raf1 = new RandomAccessFile("hello.txt", "rw");
//指针移动到3的位置
raf1.seek(3);
//raf1.seek(File.length());
raf1.write("xyz".getBytes());
raf1.close();
}
//使用RandomAccessFile实现数据插入效果
@Test
public void test3(){
RandomAccessFile raf1 = new RandomAccessFile("hello.txt", "rw");
raf1.seek(3);
//保存3后面的指针所有数据到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
byte[] buffer = new byte[20];
int len;
while((len = raf1.read(buffer)) != -1)
{
builder.append(new String(buffer, 0, len));
}
//调回指针,写入"xyz";
raf1.seek(3);
raf1.write("xyt".getBytes));
raf1.write(builder.toString().getBytes());
raf1.close();
}
85、NIO.2 中的Path、Paths、Files类的使用
New IO, Non-Blocking IO
NIO将以更高效的方式i纪念性问及那的读写操作,更加重要了
框架中经常使用
网络编程
网络中实现数据的传输
86、IP地址
127.0.0.1对应着着:Localhost;
public static void main(String[] args){ InetAddress inet1 = InetAddress.getByName("192.168.1.1"); InetAddress inet1 = InetAddress.getByName("www.atguigu.com"); InetAddress.getLocalHost(); InetAddress.getHostName/getHostAddress();}
87、TCP网络编程1
客户端发送一句话给服务端
服务端显示在控制台上
@Test public void client(){ InetAddress inet = InetAddress.getByName("127.0.0.1"); Socket socket = new Socket(inet, 8899); OutputStream os = socket.getOutputStream(); os.write("你好胖".getBytes()); os.close(); socket.close();} @Testpublic void server(){ ServerSocket ss = new ServerSocket(8899); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); //存在问题,可能会有乱码 byte[] buffer = new byte[20]; int len; while((len = is.read(buffer)) != -1) { String str = new String(buffer, 0, len); System.out.println(str); } //建议写法 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[5]; while((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos.toString()); System.out.println(socket.getInetAddress().getHostAddress()); //关闭资源 baos.close(); is.close(); socket.close(); ss.close();}
88、TCP编程例题2
客户端发送文件给服务端
@Test
public void client(){
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("a.txt"));
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != len)
{
os.write(buffer, 0, len);
}
fis.close();
os.close();
socket.close();
}
public void server()
{
ServerSocket ss = new ServerSocket(9090);
Socket socket = ss.accept;
InputStream is = socket.getInputStream();
FileOutputStrem fos = new FileOuptStream(new File("aa.txt"));
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
fos.close();
is.close();
socket.close();
ss.close();
}
89、TCP网络编程3;
返回发送成功给客户端
@Test
public void client(){
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("a.txt"));
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != len)
{
os.write(buffer, 0, len);
}
socket.shutdownOutput();
//接收服务器端的数据显示到控制台上
InputStream is = socket.getInputStream();
ByteArrayOutputStrem baos = new ByteArrayOutputStream();
byte[] buffer = new byte[20];
int len1;
while((len1 = is.read(buffer)) != -1)
{
baos.write(buffer, 0, len1);
}
System.out.println(baos.toString());
baos.close();
fis.close();
os.close();
socket.close();
}
public void server()
{
ServerSocket ss = new ServerSocket(9090);
Socket socket = ss.accept;
InputStream is = socket.getInputStream();
FileOutputStrem fos = new FileOuptStream(new File("aa.txt"));
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
//服务器端给与客户端反馈
OutputStream os = socket.getOupputStream();
os.write("你好你好");
socket.shutdownOutput();
os.close();
fos.close();
is.close();
socket.close();
ss.close();
}
90、UDP网络编程
public void sender(){ DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式发送的导弹"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(data, 0, data.length, inet, 9090); socket.send(packet); socket.close();}public void receiver(){ DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(), packet.getLength())); socket.close();}
URL编程
URL统一资源定位符,它标识Internet上某一资源的地址。
它时一种具体的URI,即URL可以用来标识一个资源
五个部分
传输协议、主机名 端口号 资源地址 参数列表
public static void main(String[] args)
{
URL url = new URL("http://localhost:8080/beauty.jpg");
url.getProtocol();//协议
url.getHost(); //主机名
url.getPort();
url.getPath();
url.getFile();
url.getQuery();//获取查询列表
URLConnection urlConnection = url.openConnection();
//访问服务器。
urlConnection.connect();
InputStream is = urlconnection.getInputStream();
FileOutputStream fos = new FileOutputStream("a.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
is.close;
fos.close();
urlConnection.disconnection();
}
反射
反射(Reflection)被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部内部结构
在运行时判断任意对象所属的类
构造任意一个对象的类
判断任意一个类所具有的成员变量和方法
获取泛型信息
调用任意一个对象的成员变量和方法
处理注解
生成动态代理
java.lang.Class .reflect.Method, .reflect.Field, .reflect.Constructor;
91、反射能干什么
//反射之前public void test(){ Person p1 = new Person("Tom", 12); p1.age = 10; System.out.println(p1.toString()); p1.show(); //不能调用私有对象和私有方法}//反射之后public void test01(){ Class clazz = Person.class; Constructor cons = class.getConstructor(String.class, int.class); Object obj = cons.newInstance("tom", 12); Person p = (Person) obj; System.out.println(obj); //通过反射,调用对象指定的属性和指定的方法 Field age = clazz.getDeclaredField("age"); age.set(p, 10); System.out.println(p); //通过反射机制调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(p); //可以调用私有方法 //私有构造器 Construct cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = (Person) cons1.newInstance("Jerry"); System.out.println(p1); //调用私有属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p, "hanMe"); System.out.println(p); //调用私有方法 Method showMethod = clazz.getDeclaredMethod("showNation", String.class); showMethod.setAccessible(true); String nation = (String) showNametion.invoke(p1, "中国");}
疑问:如何保证封装性?如何看待这两个技术
不矛盾。封装性建议我们调用public方法,反射可以让我们调用private,但是建议用public方法
疑问:通过直接new的方式或者反射方式都可以调用公共的结构,用哪个
建议用new的模式
什么时候会使用反射?
反射特征,动态性
92、关于java.lang,Class的理解
1、类加载过程:
程序经过javac.exe命令后,会生成一个或者多个字节码文件(.class结尾)
java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,这个过程就教类的加载,加载到内存中的类就叫运行时类,就作为Class类的实例
2、换句话说,Class的实例就对应着一个运行时类。
93、获取Class实例方式
public void test(){ //方式1 .class() Class clazz1 = Person.class; System.out.println(clazz1); //方式2:通过运行时类的对象 .getClass(); Person p1 = new Person(); Class clazz2 = p1.getClass(); //方式3:调用Class的静态方法 Class clazz3 = Class.forName("com.zhou.Person"); //都是同一个运行时类 System.out.println(clazz1 == clazz2); //true; System.out.println(clazz1 == clazz3); //true; //前三个需要掌握 //方式4:使用类的加载器 ClassLoader(); ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.zhou.Person"); System.out.println(clazz1 == clazz4);}
只要数组的元素类型与维度一样,就是同一个class;
94、了解类加载器
对于自定义类,使用系统类加载器进行加载
使用系统类getParent()方法,获取扩展类加载器
调用扩展类getPreant()方法, 无法获取引导类加载器;
引导类加载器主要负责加载java核心类库
public void test1(){ ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //系统加载器 ClassLoader classLoader1 = classLoader.getParent(); //扩展类加载器 ClassLoader classLoader2 = classLoader1.getParent();//获取不到引导类加载器}
95、使用classLoader加载配置文件
尽量放在src下
public void test2(){ //之前的方法,方式一 Properties pros = new Properties(); //此时的文件默认在当前的module下 FileInputStream fis = new FileInputStream("jdbc.properties"); pros.load(fis); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println(user + password);}//方式二public void test3(){ Properties pros = new Properties(); ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); //默认相对路径在当前module的src下 InputStream fis = fisclassLoader.getResourceAsStream("jdbc.properties"); pros.load(fis); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println(user + password);}
96、创建运行时类的对象
通过反射方式,创建对应的运行时类的对象
public void test1(){ Class clazz = Person.class; //newInsatnce(): //调用此方法,创建对应的运行时类的对象 //1、运行时类必须提供空参构造器 //2、空参构造器的访问权限得够,通常设置为public. Person p = (Person) clazz.newInstance();}
public Object getInstance(String classpath){ return Class.forName(classpath).newInstance;}
97、获取运行时类的属性结构及其内部结构
public void test()
{
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public的属性
Field[] fields = clazz.getFields();
System.out.println(fields);
//getDeclaredFields():
//获取当前当前运行时类中声明的所有属性,不包含父类中声明的属性
Filed[] fieldss = clazz.getDeclaredFields();
System.out.println(fieldss);
}
//属性的内容
//权限修饰符 数据类型 变量名
public void test2()
{
Class clazz = Person.class;
Filed[] fieldss = clazz.getDeclaredFields();
for(Field f : fieldss)
{
//1、权限修饰符
int modifier = f.getModifiers();
System.out.println(Modifier.toString(modifier));
//2、数据类型
Class type = f.getType();
System.out.println(type.getname);
//3、变量名
String fName = f.getName();
System.out.println(fName);
}
}
98、方法的结构
public void test()
{
Class clazz = Person.class;
//所有父类中声明为public的方法
Method[] methods = clazz.getMethods();
//Person类中所有的方法,不包含父类中声明的
Method[] methodss = clazz.getDeclaredMethods();
}
//权限修饰符 返回值类型 方法名(参数类型1) throws;
public void test2()
{
//获取方法声明的注解
Class clazz = Person.class;
Method[] methodss = clazz.getDeclaredMethods();
for(Method f : methodss)
{
//获取方法的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a : annos)
{
System.out.println(a);
}
//权限修饰符
System.out.println(Modifier.toString(m.getModifer));
//返回值类型
m.getReturnType().getName();
//方法名
m.getName();
//形参列表
m.getParameterTypes();
//抛出异常
m.getExceptionTypes();
}
}
99、构造器
public void test()
{
Class clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public的属性
Constructor[] fields = clazz.getConstructors();
System.out.println(fields);
//getDeclaredFields():
//获取当前当前运行时类中声明的所有属性,不包含父类中声明的属性
Constructor[] fieldss = clazz.getDeclaredConstructors();
System.out.println(fieldss);
}
100、运行时类的父类
public void test2()
{
Class clazz = Person.class;
//获取运行时的父类
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
//获取带泛型的父类
Type superclassa = clazz.getGenericSuperclass();
System.out.println(superclassa);
//带泛型的父类的泛型
ParameterizedType paramType = (ParameterizedType) superclasa;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments);
//getTypeName;
//(class) actualTypeArguments[0].getName();
}
101、获取运行时类实现的接口
public void test(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
}
102、获取运行时类所用的包/注解
public void test(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
clazz.getAnnotations();
}
103、调用运行时类中指定的结构
public class R{
public void testField()
{
Class clazz = Person.class;
//创建运行时类对象
Person p = (Person) clazz.newInstance();
//获取指定的属性
Field id = clazz.getField("id");
//设置当前属性值
id.set(p, 12);
int pId = (int) id.get(p);
}
}
//调用方法
public void test()
{
Class clazz = Person.class;
//创建运行时类对象
Person p = (Person) clazz.newInstance();
//1、获取指定方法
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
//参数一:方法的调用者,参数二:给方法形参赋值的实参
show.invoke(p, "chn");
}
//调用构造器
public void test()
{
Class clazz = Person.class;
//获得指定的构造器
Constructor constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
//调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("tom");
}
104、反射的应用:动态代理
//动态代理举例
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
public String getBelief(){
return "I believe I can fly!";
}
public void eat(String food)
{
System.out.println("我喜欢吃" + food);
}
}
//要想实现动态代理,需要解决的问题?
//问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
//问题二:当桶过代理类的对象调用方法时,如何东岱的去调用被代理类中的同名方法?
class ProxyFactory{
//调用此方法,返回一个代理类的对象,解决问题一
public static Object getProxyInstance(Object obj){
MyInvocationHandler handler = new MuInvocationHandler();
handler.bind(obj);
//参数一:代理类的加载器
//参数二:代理类的实现接口
//参数三:
return Proxy.newProxyInstance(obj.getClass().getClassLoader, obj.getClass().getInterfaces(), handler)
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//赋值时, 也需要使用被代理类的对象进行赋值
//当我们哦通过代理类的对象,调用方法a时,就会自动调用如下方法
//将被代理类要执行的方法a的功能就声明在invoke()中
public void bind(Object obj)
{
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
{
//代理类对象调用的方法,此方法即作为了被代理对象调用的方法。
//obj:被代理类的对象
Object returnValue = method.invoke(obj, args);
//上述方法的返回值就作为invoke方法的返回值。
return returnValue;
}
}
public class ProxyTest{
public static void main(Stirng[] args)
{
SuperMan superMan = new SuperMan();
//proxyInstance:代理类的对象
Human h = (Human) ProxyFacktory.getProxyInstance(superMan);
h.getBelief();
h.eat("四川麻辣烫");
}
}