泛型的好处: 在编译时对类型严格检查,操作类中的数据时不需要强制类型转换
泛型类数据类型参数只支持引用数据类型,不支持基本数据类型
定义泛型方法输出不同类型数组中的元素:
public static <E> void print(E[] num){
for(E n:num){
System.out.print(n+" ");
}
}
泛型方法的类型通配符:
方法的参数只能是Father或子类的类型
public static <E> void addAll(Father<? extends E> c) {
}
泛型类的类型通配符:
类名<T extends Father>
类型上限用 extends代表是自己或它的子类
类型下限用 super 代表是自己或它的父类
给定一个字符串“ok test ok am frank am ok test” 统计字符串中每个单词出现的次数,输出结果如下:
ok:3
test:2
am:2
frank:1
一个集合存储并显示如下的效果:
java
jsp
spring
python
flask
django
php
HashMap<String, List<Board>> map=new HashMap<>();
"select * from board where parentid=?"
public ArrayList<Board> select(int parentid){
}
public HashMap<String, List<Board>> findall(){
HashMap<String, List<Board>> map=new HashMap<>();
List<Board> list=select(0);
for(Board board:list){
List<Board> son=select(board.getId());
map.put(board.getName(),son);
}
return map;
}
数据结构:
线性表特点: 除首节点和尾节点外,每个节点有且只有 一个确定的前序节点,有且只有一个确定的后序节点。
队列(Queue):是一个先进先出的线性表,分为入队(offer) 和出队(poll)
常见的排序算法:
1.选择排序
public static void main(String[] args) {
int[] num={1,6,5,3,7,4,8,2};
for(int j=0;j<num.length;j++) {
int minIndex = j;
for (int i = j+1; i < num.length; i++) {
if (num[i] < num[minIndex]) {
minIndex = i;
}
}
int temp;
if(j!=minIndex){
temp = num[j];
num[j] = num[minIndex];
num[minIndex] = temp;
}
}
System.out.println(Arrays.toString(num));
}
2.冒泡排序
public static void main(String[] args) {
int[] num={4,6,5,3,7,1,8,2};
for(int j=0;j<num.length;j++) {
int temp = 0;
for (int i = 0; i < num.length- 1-j; i++) {
if (num[i] > num[i + 1]) {
temp = num[i];
num[i] = num[i + 1];
num[i + 1] = temp;
}
}
}
System.out.println(Arrays.toString(num));
}
3.插入排序:
public static void main(String[] args) {
int[] num={4,6,5,3,7,1,8,2};
for(int i=1;i<num.length;i++){
int temp;
for(int j=i;j>0;j--){
if(num[j]<num[j-1]){
temp=num[j];
num[j]=num[j-1];
num[j-1]=temp;
}
}
}
System.out.println(Arrays.toString(num));
}
或
public static void main(String[] args) {
int[] num={4,6,5,3,7,1,8,2};
for(int i=1;i<num.length;i++){
int currrent=num[i];
for(int j=i-1;j>=0;j--){
if(currrent<num[j]){
num[j+1]=num[j];
}else{
num[j+1]=currrent;
break;
}
if(j==0){
num[j]=currrent;
}
}
}
System.out.println(Arrays.toString(num));
}
java.io包中包含了对文件和目录的属性操作和对文件进行读写操作的类
File类可以对文件和目录的属性进行操作,如获得文件名、获得文件大小,不能对文件进行读写操作
方法原型 说明
String getName() 获得文件的名称
String getAbsolutePath() 获得文件的绝对路径
long length() 获得文件的长度((字节数)
public long lastModified() 返回此抽象路径名表示的文件上次修改的时间
创建一个新文件:
File file=new File("d:\\Board.java");
file.createNewFile();
创建多级文件夹:
File file1=new File("d:\\board\\20211011");
file1.mkdirs();
删除文件或文件夹: 只能从里往外删除
File file=new File("d:\\board");
System.out.println(file.delete());
判断:
boolean exists()判断文件是否存在,存在返回true,否则返回false
boolean isFile()判断是否为文件,是文件返回true,否则返回false
boolean isDirectory()判断是否为目录,是目录返回true,否则返回false
文件重命名:
File file=new File("d:\\board\\20211011\\Board.java");
File dest=new File("d:\\board\\20211011\\Boards.java");
file.renameTo(dest);
File对盘符可以进行操作:
File file=new File("c:");
System.out.println(file.getTotalSpace()/1024/1024/1024);
System.out.println(file.getFreeSpace()/1024/1024/1024);
System.out.println(file.getUsableSpace()/1024/1024/1024);
获得盘符或文件夹中的所有文件和文件夹:
File file=new File("c:");
System.out.println(Arrays.toString(file.list()));
获得文件名和文件大小:
File file=new File("D:");
for(File temp:file.listFiles()){
System.out.println(temp.getName()+" "+temp.length()/1024+"KB");
}
流分为输入流和输出流 也分为字节流和字符流
分别用4个抽象类表示:
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流
写入字符流:
public static void main(String[] args) throws IOException {
Writer writer=new FileWriter("d:\\board\\20211011\\test.txt");
writer.write("现在学习很痛苦");
writer.close();
}
追加写入字符流:
Writer writer=new FileWriter("d:\\board\\20211011\\test.txt",true);
节点流的文件复制:
public static void main(String[] args) throws IOException {
Reader reader=new FileReader("d:\\board\\20211011\\test.txt");
Writer writer=new FileWriter("d:\\board\\20211011\\newtest.txt");
char[] temp=new char[10];
int length=reader.read(temp);
StringBuffer stringBuffer=new StringBuffer();
while(length!=-1) {
String s = new String(temp);
stringBuffer.append(s);
temp=new char[10];
length=reader.read(temp);
}
reader.close();
writer.write(stringBuffer.toString());
writer.close();
System.out.println("文件复制成功");
}
处理流的文件复制:
public static void main(String[] args) throws IOException {
Reader reader=new FileReader("d:\\board\\20211011\\test.txt");
Writer writer=new FileWriter("d:\\board\\20211011\\buffertest.txt");
BufferedReader bufferedReader=new BufferedReader(reader);
BufferedWriter bufferedWriter=new BufferedWriter(writer);
String line=bufferedReader.readLine();
while (line!=null){
bufferedWriter.write(line);
bufferedWriter.newLine();
line=bufferedReader.readLine();
}
bufferedReader.close();
reader.close();
bufferedWriter.close();
writer.close();
}
字节流的写入(节点流):
OutputStream outputStream=new FileOutputStream("d:\\board\\20211011\\stream.txt",true);
String s="abc123<html>今日天气";
outputStream.write(s.getBytes());
outputStream.close();
字节流的写入(处理流):
OutputStream outputStream=new FileOutputStream("d:\\board\\20211011\\stream.txt",true);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
String s="abc123<html>今日天气";
bufferedOutputStream.write(s.getBytes());
bufferedOutputStream.close();
outputStream.close();
字节流的读取(节点流):
InputStream inputStream=new FileInputStream("d:\\board\\20211011\\stream.txt");
byte[] bytes=new byte[1024];
int length=inputStream.read(bytes);
while (length!=-1){
String s=new String(bytes);
System.out.print(s);
bytes=new byte[1024];
length=inputStream.read(bytes);
}
inputStream.close();
字节流的读取(处理流):
InputStream inputStream=new FileInputStream("d:\\board\\20211011\\stream.txt");
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
byte[] bytes=new byte[1024];
int length=bufferedInputStream.read(bytes);
while (length!=-1){
String s=new String(bytes);
System.out.print(s);
bytes=new byte[1024];
length=inputStream.read(bytes);
}
bufferedInputStream.close();
inputStream.close();
字符到字节的转换流:
OutputStream outputStream=new FileOutputStream("d:\\board\\20211011\\streamTransfer.txt");
OutputStreamWriter writer=new OutputStreamWriter(outputStream);
writer.write("abc");
writer.close();
outputStream.close();
RandomAccessFile 随机流,支持对随机访问文件的读取和写入
getFilePointer方法获得光标的位置
seek方法设置光标位置
随机流读取文件:
RandomAccessFile randomAccessFile=new RandomAccessFile("d:\\board\\20211011\\streamTransfer.txt"
,"r");
randomAccessFile.seek(1);
byte[] bytes=new byte[1024];
int length=randomAccessFile.read(bytes);
while (length!=-1){
String s=new String(bytes);
System.out.println(s);
bytes=new byte[1024];
length=randomAccessFile.read(bytes);
}
randomAccessFile.close();
随机流写入文件:
RandomAccessFile randomAccessFile=new RandomAccessFile("d:\\board\\20211011\\streamRandom.txt"
,"rw");
String s="12345678abc";
randomAccessFile.write(s.getBytes());
randomAccessFile.close();
反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
得到Class类的对象的3种方式:
1. Class c=Class.forName("包名.类名")
2. Class c=类名.class
3. Class c=对象名.getClass()
反射方式Method类的常用方法:
getName() 获得方法名
getReturnType() 获得方法返回值
Arrays.toString(method.getParameterTypes()): 获得方法参数列表
invoke(对象名,参数) : 通过反射调用类中的方法
案例: 通过反射验证没有生成set方法的属性名
注解:是一种标识,在机器没有执行代码之前通过注解告诉机器代码的功能
语法: public @Interface 注解名字{}
空间范围: 包package 类type 方法method 属性field 局部变量local_variable 方法参数parameter 构造方法 constructor
时间范围: source 表示该注解只在源代码级别保留,编译时就会被忽略;
class 表示该注解编译时被保留,在class文件中存在,但JVM将会忽略;
runtime 表示该注解被JVM保留,所以能在运行时被JVM或其他使用反射机制的代码所读取和使用.
注解属性: 看起来像个方法,其实是属性,属性类型包括所有基本类型、String、Class、enum、Annotation、以上类型的数组形式
进程: 运行中的应用程序,如运行QQ
进程只是分配空间内存,具体代码是由线程来执行。
多线程: 是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务
多线程优点:
•Java支持编写多线程的程序;
•多线程最大的好处在于可以同时并发执行多个任务;
•多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率。
多线程缺点:
•线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
•多线程需要协调和管理,所以需要CPU时间跟踪线程;
•线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
•线程太多会导致控制太复杂,最终可能造成很多Bug
创建线程的3种方式:
1.继承Thread父类
2.实现Runnable接口
3.实现Callable接口
继承Thread父类代码:
public class MyThread extends Thread{
public void run(){
for(int i=0;i<=100;i++){
System.out.println(this.getName()+"打印: "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread threadA=new MyThread();
threadA.setName("线程A");
MyThread threadB=new MyThread();
threadB.setName("线程B");
threadA.start();
threadB.start();
}
}
实现Runnable接口代码:
public class RunThread implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.println(Thread.currentThread().getName()+"打印: "+i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
RunThread t=new RunThread();
Thread threadA=new Thread(t);
threadA.setName("线程A");
Thread threadB=new Thread(t);
threadB.setName("线程B");
threadA.start();
threadB.start();
}
}
创建线程的三种方式-比较
•继承Thread类:
•优势:Thread类已实现了Runnable接口,故使用更简单
•劣势:无法继承其它父类
•实现Runnable接口:
•优势:可以继承其它类
•劣势:编程方式稍微复杂,多写一行代码
•实现Callable接口:
•类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的,方法可以有返回值,并且可以抛出异常。但是Runnable不行
•Java中线程存在以下几种状态:
•新线程: 新创建了一个线程对象,此时它仅仅作为一个对象实例存在,JVM没有为其分配CPU时间片和其他线程运行资源。
•就绪状态: 在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,
只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会。
•运行状态: 就绪态的线程获得cpu就进入运行态。
•等待/阻塞:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
•死亡状态:线程执行完它的任务时,由JVM收回线程占用的资源。
Thread类中的常用static方法:
static Thread currentThread()返回对当前正在执行的线程对象的引用
static void sleep(long millis)throws InterruptedException使当前正在执行的线程休眠(暂停执行)
static void yield()让出时间片
static boolean interrupted()判断当前线程是否已经中断
Thread类中的常用实例方法:
final void setPriority(int newPriority)设置线程的优先级
final boolean isAlive()判断线程是否处于活动状态
void interrupt()中断线程
线程启动:执行start()方法,不能多次执行
阻塞的线程提前消亡:用interrupt()方法抛出异常,执行catch块
获得线程名的两种方式:
1.类实现Runnable时使用Thread.CurrentThread().getName()
2.类继承Thread时用getName()
设置线程的优先级:
通过setPriority()和getPriority()可以设置或返回优先级
线程优先级用1-10表示,10最大,默认是5
3个静态常量也可以表示优先级,MAX_PRIORITY是10,MIN_PRIORITY是1,NORM_PRIORITY是5
线程分为两类:用户线程和守护线程
守护线程:是所有用户线程都消亡后,最后一个被杀死的线程。
sleep线程休眠用法: 不能保证该线程睡眠到期后就开始执行。
sleep yield
暂停后的状态 进入被阻塞的状态,直到经过指定时间后,才进入可运行状态 直接将当前线程转入可运行状态
没有其他等待运行的线程 当前线程会等待指定的时间后执行 当前线程有可能会马上恢复执行
等待线程的优先级别 不考虑优先级 优先级相同或更高的线程运行
线程的join()方法:强制让其他线程等待,直到当前线程执行结束
线程的join(int 毫秒)方法:强制让其他线程等待多少毫秒
通过线程同步实现3个窗口卖100张票:
public class TicketThread extends Thread{
public static int num=100;
public static Object object=new Object();
public void run(){
while (true){
synchronized (object){
if(num>0){
System.out.println(this.getName()+"卖了第"+num+"张票");
num--;
}else{
System.out.println("票已卖完");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
TicketThread t1=new TicketThread();
t1.setName("窗口A");
TicketThread t2=new TicketThread();
t2.setName("窗口B");
t2.setPriority(6);
TicketThread t3=new TicketThread();
t3.setName("窗口C");
t1.start();
t2.start();
t3.start();
}
}
同步的前提
•同步需要两个或者两个以上的线程。
•多个线程使用的是同一个锁
未满足这两个条件,不能称其为同步。
同步的弊端
性能下降 会带来死锁
同步的优势
解决了线程安全问题,对公共资源的使用有序化
同步注意
•不要盲目对不需要同步的代码使用Synchronized,
以避免影响性能和效率。
线程死锁:两个线程互相持有对方依赖的共享资源,造成都无限阻塞。
线程通讯: 多个线程通过消息传递实现相互调度,即线程间相互作用。
线程通讯的常用方法:
notify(): 唤醒单个线程 notifyall() 唤醒所有线程
wait() 当前线程等待,直到被别的线程唤醒
wait(int 毫秒) 当前线程等待,直到被别的线程唤醒或超出等待时间
wait()和notify()一定要和synchronized一起使用,如果同步锁的对象是obj, 使用obj.wait()和obj.notify()
阻塞状态的3种情况:
同步阻塞: 线程获得同步锁,,若被别的线程占用,线程放入锁池队列中
•等待阻塞 线程执行wait()方法,线程放入等待队列中。某个线程取得对象锁;
•其它阻塞 线程执行sleep()或join()或发出I/O请求。
线程池: 我们把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程。
只要池中有空闲线程,任务就会分配给一个线程执行。
通过线程池创建5个线程:
public static void main(String[] args) {
ExecutorService executorService= Executors.newCachedThreadPool();
for(int i=1;i<=5;i++){
int index=i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.execute(new Runnable() {
@Override
public void run() {
Thread.currentThread().setName("线程"+index);
System.out.println("当前第"+index+"个线程,线程名: "+Thread.currentThread().getName());
}
});
}
}
信号量:为多线程协作提供了更为强大的控制方法。信号量是对锁的扩展。
锁一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。
通过信号量实现一次5个线程的锁定:
public class SemapThread implements Runnable{
Semaphore semaphore=new Semaphore(5);
@Override
public void run() {
try {
semaphore.acquire();
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"完成");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SemapThread semapThread=new SemapThread();
ExecutorService executorService= Executors.newFixedThreadPool(20);
for(int i=1;i<=20;i++){
executorService.execute(semapThread);
}
executorService.shutdown();
}
}
设计模式:就是解决某类特定问题固定的解决方式。
设计模式分别3大类:
创建型模式: 单例模式、工厂模式
结构型模式: 代理模式
行为型模式: 观察者模式
单例模式: 一个类只能有一个实例,用于频繁创建一个对象的场景
实现方式: 1.定义私有构造方法
2.声明static的类的对象
3.提供公有static方法返回类的对象
具体代码(懒汉式):
public class SingletonClass {
private static SingletonClass singletonClass;
private SingletonClass(){
}
public static SingletonClass getSingletonClass(){
if(singletonClass==null){
singletonClass=new SingletonClass();
}
return singletonClass;
}
}
饿汉式:
public class SingletonClass {
private static SingletonClass singletonClass=new SingletonClass();
private SingletonClass(){
}
public static SingletonClass getSingletonClass(){
return singletonClass;
}
}
工厂模式:是为创建对象提供过渡接口。
工厂模式分为: 简单工厂模式、工厂方法模式、抽象工厂模式
简单工厂的实现方法:
public interface DB { ------抽象产品
void insert();
}
public class MySQLDB implements DB{ -----具体产品1
@Override
public void insert() {
System.out.println("对MySQL数据库添加数据");
}
}
class OracleDB implements DB{ -----具体产品2
@Override
public void insert() {
System.out.println("对Oracle数据库添加数据");
}
}
class Factory{ ----工厂类
public static DB create(String type){ -----生产产品的方法
DB db=null;
switch (type){
case "mysql":
db=new MySQLDB();
break;
case "oracle":
db=new OracleDB();
break;
}
return db;
}
}
class Test{
public static void main(String[] args) {
DB db=Factory.create("mysql");
db.insert();
}
}
抽象工厂:
抽象工厂: 相当于中软北京总部
具体工厂: 相当于长春ETC或大连ETC
抽象产品: 相当于课程体系、课件
具体产品: 相当于长春ETC的学员
观察者模式:
每个对象在发生改变时,其他对象能观察到对象的改变并作出响应。
ObServer: 是被观察者
ObServeable : 是被观察者类
setChanged()方法: 设置一个标志注明数据发生了变化
notifyObservers(): 通知观察者数据发生了变化,自动调用update方法
具体代码:
//被观察者
public class MyOBServerAble extends Observable {
private int data;
public int getData() {
return data;
}
public void setData(int data) {
if(this.data!=data){
this.data = data;
setChanged();
notifyObservers();
}
}
}
//观察者
public class MyOBServer implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("数据发生变化");
}
public MyOBServer(MyOBServerAble myOBServerAble){
myOBServerAble.addObserver(this);
}
}
class TestAb{
public static void main(String[] args) {
MyOBServerAble myOBServerAble=new MyOBServerAble();
MyOBServer myOBServer=new MyOBServer(myOBServerAble);
myOBServerAble.setData(10);
}
}
代理模式:给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。
静态代理: 代理对象和被代理对象是绑定关系,固定不换
动态代理: 代理对象和被代理对象经常更换。
java对象序列化: 在保存对象时,会把对象的状态保存为一组字节,在未来再将这些字节组成对象
对象的序列化保存的是对象的状态,即成员变量
在java中,只要一个类实现了java.io.Serializable接口,就表明这个类可以被序列化
在类的某个字段中加入transient,当前字段就不会被序列化。 private transient String address;
为了保证序列化和反序列化的版本一致加入: private static final long serialVersionUID=1L;
垃圾回收器运行在堆内存上
•Java垃圾回收是一个自动运行的管理程序,运行时使用内存的进程。
System.gc()和Runtime.gc()可以手动调用gc,但不会马上执行。
GC主要处理的是对象的回收操作,那么什么时候会触发一个对象的回收的呢:
•对象没有引用
•作用域发生未捕获异常
•程序在作用域正常执行完毕
•程序执行了System.exit()
•程序发生意外终止(被杀进程等)
内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着
finalize()方法是object类中的方法,提供在对象被收回时调用以释放资源。