JAVA进阶内容

泛型

泛型类

往往我们在写类的时候里面的属性可能会存在多个参数类型同时我们有的时候输出需要的类型与实际类型不同的时候还需要强制转化的情况,特别是在建立链表,散列表的时候

我们可以使用泛型来定义一个类

其定义格式

	访问权限 class 类名<泛型,泛型>{
	属性;
	方法
	}

创建对象

类名 <具体类型> 对象名=new 类名<具体类型>();

泛型<>始终在类名后面

优点:在使用这些泛型类建立数据结构时,不必进行强制转换

public class study1 {
    public static void main(String[] args) {
        System.out.println("第一节课学习面向对象创建maven对象");
        Point point=new Point();
        point.setX(10);
        point.setY(10);
        int px=(Integer)point.getX();
        int py=(Integer)point.getY();
        System.out.println("x="+px+"  y="+py);
    }
}
class Point{
    private Object x;
    private Object y;

    public void setX(Object x) {
        this.x = x;
    }

    public void setY(Object y) {
        this.y = y;
    }

    public Object getY() {
        return y;
    }

    public Object getX() {
        return x;
    }
}


按照原来的方法我们每次要获取值时都要根据需要强制转换一下,这可能造成我们的数据有误差而当我们使用泛型则避免了强制转换,我们只需要在创建的时候更改我们的泛型就可以了

public class study1 {
    public static void main(String[] args) {
        System.out.println("第一节课学习面向对象创建maven对象");
        Point<String> point=new Point<String>();
        point.setX("10");
        point.setY("10");
        System.out.println("x="+point.getX()+"  y="+point.getY());
    }
}
class Point<T>{
    private T x;
    private T y;

    public void setX(T x) {
        this.x = x;
    }

    public void setY(T y) {
        this.y = y;
    }

    public T getX() {
        return x;
    }

    public T getY() {
        return y;
    }
}


构造方法使用泛型

当一个类是泛型类,我们在邪气构造方法时这样

Point(T x,T y){
x=10;
y=1o;
}

这是写的上面的构造方法

通配符?

import javax.sound.sampled.DataLine.Info;

public class study1 {
    public static void main(String[] args) {
       Into<String> i=new Into<String>();
       i.setKey("hahahahaha");
       tell(i);
    }
    public static void tell(Into<?> i){
        System.out.println(i);
    }
}
class Into<T>{
    private T key;

    public T getKey() {
        return key;
    }

    public void setKey(T key) {
        this.key = key;
    }
    @Override
    public String toString(){
        return this.getKey().toString();
    }
}

这是课堂上的一个例子

这里通配符?与泛型结合使用,用于解决方法参数多类型的情况,这样我们可以避免重载方法实现任意类型的参数传入

泛型接口

申明方式与泛型类基本一致,只是在接口名后添加<>

格式:

interface 接口名称<泛型标识>{}
import javax.sound.sampled.DataLine.Info;
interface GenInter<T>{
    public  void say();
}
class Gin implements GenInter<String>{
    private String info;
    public Gin(String info){
        this.info=info;
    }

    @Override
    public void say() {

    }

    public void setInfo(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }


}
public class study1 {
    public static void main(String[] args) {
        Gin g = new Gin("sadsadada");
        System.out.println(g.getInfo());
    }
}

这是接口中泛型的应用

当然这里我们在使用接口的时候就定义了其类型为String我们也可以不指定而是留到主方法中指定

import javax.sound.sampled.DataLine.Info;
interface GenInter<T>{
    public  void say();
}
class Gin<T> implements GenInter<T>{
    private String info;
    public Gin(String info){
        this.info=info;
    }

    @Override
    public void say() {

    }

    public void setInfo(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }


}
public class study1 {
    public static void main(String[] args) {
        Gin<String> g = new Gin<String>("sadsadada");
        System.out.println(g.getInfo());
    }
}

这样也是一样的

泛型方法

泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型

定义泛型方法比较好理解,泛型就是用来代替数据类型的,我们用泛型代替其返回值类型,这就直接解决了多种参数类型的问题-不用再重载方法了

class Gener{
	public <T>T tell(T t){
	return t;
	}
}

使用和一般的方法使用是一样的

泛型数组

在使用泛型方法的时候也可以传递或者返回一个泛型数组

public static <T>void tell(T arr[]){
    for(int i=0;i<arr.length;i++){
        System.out.println(arr[i]);
    }
}

我们可以给这个方法传入任意类型的数组类型

这就是使用泛型方法传递的泛型数组

接口集合

Collection

称其为集合,可以理解为一个动态的数组,不同的集合中的对象内容可以任意扩充Collection的常用子类有list/set/queue

里面含有很多已经写好的方法可以直接用,具体的可查看JDK_API或者网上查吧

List

List接口可以存放任意的数据,而且在List接口中内容是可以重复的

List接口的常用子类:

  1. ArrayList
  2. Vector

(注意接口是不能直接实例化的,我们要通过其子类使用)

package javastudy;

import java.util.ArrayList;
import java.util.List;

public class ListDemo01 {
    public static void main(String[] args) {


        List<String> lists = null;
        lists = new ArrayList<String>();
        lists.add("A");
        lists.add("b");
        for (int i = 0; i < lists.size(); i++) {
            System.out.println(lists.get(i));
        }
    lists.remove(0);
        for (int i = 0; i < lists.size(); i++) {
            System.out.println(lists.get(i));
        }
    }

}

这就是通过使用List接口的子类ArrayList来添加删除显示数据

Set

set接口中不能加入重复元素,但是可以排序

set接口的常用子类:

  1. 散列存放HashSet
  2. 有序存放:TreeSet

用法和上面的基本类似

package javastudy;

import java.util.HashSet;

public class ListDemo01 {
    public static void main(String[] args) {


        HashSet<String> s =null;
        s=new HashSet<String>();
        s.add("A");
        s.add("B");
        s.add("C");
        s.add("D");
        System.out.println(s);

    }

}

image-20220305210311039

Iterator接口

1.集合输出的标准操作:
一般只要是集合都要使用Iterator来进行输出

实例化和其他的接口有点不一样,其他是通过其子类实例,而他没有子类,直接可以通过方法调用

image-20220306095907473

需要注意的是其remove方法有点特殊不能用集合直接去调用remove方法,

package javastudy;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> lists=new ArrayList<>();
        lists.add("A");
        lists.add("B");
        lists.add("E");
        lists.add("G");
        lists.add("J");
        Iterator<String> iter=lists.iterator();
        while (iter.hasNext()){
            String str=iter.next();
            if ("A".equals(str)){
                iter.remove();
            }else {
                System.out.println(str);
            }
        }
    }
}

image-20220306101253264

Map接口

保存形式不一样

key->value通过键值对的形式保存

常用子类:

HashMap:无序存放,key不允许重复

Hashtable:无序存放,key不允许重复

通过put存放值

package javastudy;

import java.util.HashMap;
import java.util.Map;

public class MapDeom {
    public static void main(String[] args) {
        Map<String,String> map=new HashMap<String,String>();
        map.put("key1","study0");
        map.put("key2","study1");
        map.put("key3","study2");
        map.put("key4","study3");
        map.put("key5","study4");
        map.put("key6","study5");
        String str=map.get("key1");
        System.out.println(str);

    }
}

其他的方法使用可以查看为文档,如判断key或value是否存在的方法

image-20220306103406176

返会所有的键

image-20220306103550335

IO流

基本上就是输入输出

FileInputStream

用于读取文件中的数据

image-20220312160525799

FileOutputStream

用于创建并且写入数据

image-20220312160854072

一般如上两个需要结合使用,这样就可以完成字节流的复制

BufferedInputStream/BufferedOutputStream

用与带缓存的读取和输出用与提高上面两个字节流的运行速率,以下代码是四种类的综合使用来完成文件的拷贝

image-20220312161516486

(注意:这里运行完了都要手动关闭,哪个后定义就要先调用close

InputStreamReader/OutputStreamWriter

用与字符流向字节流数据的转化在读取和写入时选着使用

BufferedReader/BufferedWriter

带缓存的读取和写入,使用后要手动调用.flush

也可以使用PrintWriter来写入这样就可以用close关闭

image-20220312162752376

FileReader/FileWriter

用与对普通文件的读入写入这样就不用管是字节流还是字符流,比较方便就不用转化数据了

也可以联合使用带缓冲的方式来优化效率

RandomAccessFile随机文件读写

这个类是通过指针在特定放的位置对文件进行读写操作,具体的方法可以查看API

image-20220314170423172

image-20220314170507859

这里的文件分为5个区块

image-20220314171051777

传入的block是从1开始的对应图片的上面一排,而文件的指针是从0开始的,我们定义一个区块的大小L时100,所以第一区块可以表示为0*L,第二区块表示1*L,所以我们在代码中用的是raf.seek((block-1)*L)这个来表示当前线程所处区块的头部

public class WriteFile extends Thread{
    File file;
    int block;
    int L=100;
    public WriteFile(File f,int b) {
    this.file=f;
    this.block=b;
    }
    @Override
    public void run() {
        try {
            RandomAccessFile raf=new RandomAccessFile(file, "rw");
            try {

                    raf.seek((block - 1) * L);
                    raf.writeBytes("ssssss" + block);
package javastudy;

import java.io.File;

public class TestRandomAccessFile {
   static File file=new File("text.txt");
    public static void main(String[] args) {
    if(file.exists()){
        file.delete();
    }
    new WriteFile(file,1).start();
    new WriteFile(file,2).start();
    new WriteFile(file,3).start();
     new WriteFile(file,4).start();
    new WriteFile(file,5).start();
    }
}

文件写入主要代码如上

 RandomAccessFile raf=new RandomAccessFile(file, "r");
 try {
  raf.seek(400);
 } catch (IOException e) {
  e.printStackTrace();
 }
 byte[] str=new byte[15];
 try {
  raf.read(str);
  String in=new String(str);
  System.out.println(in);

 } catch (IOException e) {
  e.printStackTrace();
 }
} catch (FileNotFoundException e) {
 e.printStackTrace();
}

读取代码如上只用更改主方法就可以了

使用Apache Io库操作IO与文件

在浏览器中搜索apache.org

选择commonsimage-20220314221526281在选择IO下载相应的jar包,这个是一个工具包,导入到工程之后有许多可以操作IO的类可以直接使用,为我们IO操作提供了便捷

image-20220314221830202

其中比较常用的是image-20220314222019026

比如我们要操作文件我们直接实现FileUtils类就可以了,下面代码将文件提取为字符串

package javastudy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

public class TestFileUtils {
    public static void main(String[] args) {
        File file=new File("unite.txt");
//        File file2=new File("text.text");
        try {


          String input=FileUtils.readFileToString(file,"UTF-8");
            System.out.println(input);


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这就比我们自己写方便得多

我们要拷贝文件直接用一句就搞定了

FileUtils.copyFile(file, file2);

多线程

线程与进程的区别

image-20220314103541859

线程的实现

1.继承Thread类

2.实现Runnable接口

Thread类实现

1.Thread类是在Java.lang包中定义,继承Thread类必须重写run()方法

具体使用方法可以查询API

定义格式

class className extends Thread{
	run(){
	重写的方法体
	}
}

线程的启动不是调用你重写的run方法,而是通过start()方法启动

package javastudy;

public class MyThread extends Thread{
    private String name;
    public MyThread(String name){
        this.name=name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(name+":"+i);
        }
        super.run();
    }
}
package javastudy;

public class ThreadDemo01 {
    public static void main(String[] args) {
        MyThread t1=new MyThread("A");
        MyThread t2=new MyThread("B");
        t1.start();
        t2.start();
    }
}

运行后你会发现AB两线程是并发执行的

image-20220314105149385

Runnable接口实现

Runnable接口中没有start方法,所以我们在用了Runnable接口后也要通过Thread来调用线程

package javastudy;

public class MyRunnable implements Runnable{
    private String name;
    public MyRunnable(String name){
        this.name=name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(name+":"+i);

        }
    }
}
package javastudy;

public class ThreadDemo01 {
    public static void main(String[] args) {
//        MyThread t1=new MyThread("A");
//        MyThread t2=new MyThread("B");
//        t1.start();
//        t2.start();
        MyRunnable r1=new MyRunnable("A");
        MyRunnable r2=new MyRunnable("C");
        Thread t1=new Thread(r1);
        Thread t2=new Thread(r2);
        t1.start();
        t2.start();

    }
}

线程的状态

image-20220314110510669

线程的常用方法

线程的常用方法主要都在Thread类里这些方法都需要用Thread.来调用

  1. 获取线程名称getName()

  2. 取得当前线程对象currentThread()

  3. 判断线程是否启动isAlive()

  4. 线程的强行运行join()

  5. 线程的休眠sleep()

  6. 线程的礼让yield()

    线程的优先级

    线程的优先级有可能会影响线程的执行顺序

image-20220314151329634

提高优先级通过Thread对象.setPriority(Thread.MIN_PRIORITY)来更改对象的优先级,这样可能让优先级高的线程先获取cpu资源

同步与死锁

同步代码块:在代码块上加上"synchronized"关键字,则此代码块就称为同步代码块

同步代码块格式:

synchronized(同步对象){

需要同步的代码块

}

除了代码块可以同步,方法也是可以同步的,格式:

synchronized void 方法名称(){}

这里有个例题就是三个车票厅同时买五张票如果我们用三个线程来当三个车票厅,并发买票,这样的话就会出现重复的情况,当我们将票共享后就不会出现重复情况了

package javastudy;
class MyThread implements Runnable{
    private int ticket=5;
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (this) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("车票:" + ticket--);
                }
            }
        }
    }
}
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyThread m=new MyThread();
        Thread t1=new Thread(m);
        Thread t2=new Thread(m);
        Thread t3=new Thread(m);
        t1.start();
        t2.start();
        t3.start();
    }
}

image-20220314154530593

package javastudy;
class MyThread implements Runnable{
    private int ticket=5;
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {

                if (ticket > 0) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("车票:" + ticket--);
                }
            }
        }
    }
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyThread m=new MyThread();
        Thread t1=new Thread(m);
        Thread t2=new Thread(m);
        Thread t3=new Thread(m);
        t1.start();
        t2.start();
        t3.start();
    }
}

image-20220314154644585

当我们多线程要共同操作同一个数据时我们就要将这个数据对应的代码块或者方法同步化

将上面代码改成同步化方法的形式

package javastudy;
class MyThread implements Runnable{
    private int ticket=5;
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            tell();

            }
        }
        public synchronized void tell(){
            if (ticket > 0) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("车票:" + ticket--);
            }
        }
    }
public class ThreadDemo02 {
    public static void main(String[] args) {
        MyThread m=new MyThread();
        Thread t1=new Thread(m);
        Thread t2=new Thread(m);
        Thread t3=new Thread(m);
        t1.start();
        t2.start();
        t3.start();
    }
}

死锁就是两个线程的运行条件矛盾了,将第一个线程的条件一般就能解除死锁

线程的生命周期

image-20220314155610235

点击查看代码

posted @ 2022-03-14 22:39  Ember00  阅读(45)  评论(0)    收藏  举报