欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java整理笔记基础 IO 面向对象 多线程 容器

程序员文章站 2024-03-08 16:46:22
...

JAVA整理

一、java基础

JAVA优势

  1. 面向对象 ,更接近于现实世界,有利于大型系统的建设。
  2. 平台无关性
  3. 安全性,强类型,垃圾回收,异常处理,安全检测

java 是解析性语言。 编译性的快。 java 到处是指针,


java 类型

基本类型和引用类型

  1. 类型转换

    1.1 自动转换规则:

    Java整理笔记基础 IO 面向对象 多线程 容器

    char a= 'A'  
        int b =a ; //自动 转换
    当使用 “+=”时不会产生自动类型转换。
    

    1.2 强制 类型转换

    ()加括号,精度损失。

    short s1=1;
    s1=s1+1;//错误 ,因为1 是int 类型。 s1+1 的结果 是 int 类型,而s1是short类型,是大转小,会报错。
    s1+=1; //正确,因为这个隐式包含了 强转(short)
    double a =25/2 //没有问题,自动 转高类型是没有问题的。
    
   
   char 能否存储汉字?
   
   能。char 两个字节。 java使用Unicode 16位编码。
  1. 不可变类 Immutable class

    定义: 实例化后。在生命周期内不可修改值 。

    如字符串赋值的时候,是把引用给它,而不是去修改它的值。

    最大的优点是解决:线程安全的问题。

    如何定义一个不可变类,:

    ```java
       Final class  xxx{//Final 关键字
           private  成员变量
               没有setxxx方法
               可以get 方法。
               xxx(){
               构造方法 对于非不可变类,引用类型的,要深拷贝 。  array.clone();
           }
       }
    

    运算符

    ±*/ = 位 > < = ?: && || ++ – ~ >> <<

    先加后加 ,先加了再进行表达式运算, 单目运算符。

    没有什么用,。
    += 类的表示 先加再=

?: 如果 则 否

运算符的优先级。

  1. 分隔符 () []

  2. 单目运算 ! +(正) -(负) ~ ++ –

  3. 乘除 余 */ %

  4. 加减

  5. 移位

  6. 比较

  7. 按位与

  8. 条件 与

  9. 条件或

  10. 三目运算 ?:

  11. 赋值 = += >>>= 等

2和14 是从右向左 其它是 从左向右

位运算 效率高 。如2*8 可以 将2左 移3位。2 的n次方。

== 和 equals 有什么区别 。

== 两个变量值 是否相等。 字符串则是 比较的是指向内容,而不是 指针本身。

注意 如果一个类中没有定义 equals 方法,那么 将会继承 Object 中的equals 方法。也就是 与== 效果是 一样的。

boolean equals (Object o){
    return this ==o
}

分支语句

java 8后 switch 支持了 string

foreach 会 将集合的 元素或collection 值 赋 给变量。

弃用goto

跳出循环:

break: 结束整个语句体。

continue: 结束本次循环。

java 还支持 break label 标签功能 。指定哪个 for

return ; 在方法体中用。

数组

复制 数组: clone 方法。 for 方法。

方法三:System.arraycopy()方法。

方法四:Arrays.copyof()方法

如果数组内的元素类型是 不是基本类型则不是深拷贝。要用对象 的深clone方法。

实现 Cloneable 类。

数组 有length属性 没有length方法。

string 有length方法。

字符串

String 是基本的类。

StringBuffer 类 的对象 是代表一个字符序列可变的字符串对象 。可以通过 它 提供的append 、insert 、reverse 等方法。

StringBuilder : 没有线程安全机制,所以性能高。

正则表达式。

异常处理

catch 抓

finally 代码块 。由于异常机制,会保证这个finally总会被执行(无论是否发生了异常。哪怕return 写在了 catch里面。 写在try之前的return不算哦) 。也可以用于收尾工作。 和释放资源的。

百度搜索 异常继承图。

checked与 runtime 异常区别 :

checked 如 io 不可避免,runtime 程序员错误 。

try{
    return 1;
    
}catch(){
    return 2;
    
}
finally(){
    return 3;
    
}
返回的将会是 return 3 ;!!!!

finally 一定会执行吗?

  1. finally在 try之前 发生肯定不会执行。

  2.  try{
         System.exit(0);
         return 1;
     }catch(){}
    finally(){} //当在try中被强制退出时,是不会执行的。
    

throws 是用在方法 上的

fun xxx () throws xxxE{} //交给调用者处理。

反射机制

在JAVA中使用一个类,得先将该类加载到内在中 ,为该类生成 java.lang.Class 的实例

这个Class对象作用很大,通过 它可以访问到JVM中该类的信息。也是反射的核发要素。

Class <test> classTest=test.class;
// 可以看到classTest 的类型是Class 类型 对象。 
这样就可以获得test 类的全部信息了。
    Class.forName() 可以得到Class对象。

工厂模式经常这么干。

  • 问题:反射机制的基本概念,什么是反射机制。提供了什么功能 ,哪里用得到?

  • 答:使用中改变 结构叫动态语言。java 运行时动态加载 并使用编译期时并不知道的类,则。

  • 功能:获得类的信息。包括方法和构造。。可以动态生成对象 。

  • 用处:工厂模式。spring mybatis

  • 缺点:破坏了类的封装性。私有方法。不安全,性能差。

关键字

final

修饰变量时:表示该属性不可变,

修饰方法时: 方法不可被覆盖。‘

修饰类时:不可被继承。

finalize

在垃圾回收一个对象的时候,会调用回收对象 的 finalize方法。

在非JAVA资源时,如关闭文件句柄,要用到。

finalize不一定会执行,也不知道什么时候会执行。 所以要想保证 类中打开的资源一定被清理,就不要 放在 finalize中执行。

Static

只有一个,不管实例多少。类加载时就会被分配内存。

  1. static 代码块,一般初始化。
  2. static 内部类,不依赖外部类实例而被实例化。
volatile

java 内存机制

Java整理笔记基础 IO 面向对象 多线程 容器

工作内存:有点像高速缓存。不是主存。每个线程都有自己的工作内存

所有对变量的操作的工作都要在 工作内存中完成 再存到主存。

理论应当两个地方的值是相等的。

为什 么,因为 效率高,工作地方独立。便于存取数据。

缺点:数据不一致性。

在某一刻 。A线程一 在工作内存要改变变量的值。没来得及存入主存,就被让出了cpu 占有权。另一线程 B拿到cpu, 读取到 了 自己工作内存的值(旧的值)。

之前的线程再运行的话,就会导致 两个 新旧 的值。

volatile 关键字的作用:

  • 强制 将修改的值立即写入主存。??(难道原来不是立即的吗?)

  • 但线程修改变量值时,会使该变量在任何线程中暂时无效,迫使它从主存中直接读取该值(因为原先是从自己工作内在中读的。)。

    为什么 可以无效呢。因为上面说了,线程B是先读取自己的工作内存,如果,发现 工作内存中的值无效了,才会去 主存中读。而volatile ,不管无效有效 都从主存中更新。

可见性,每一步操作对另一线程是可见的。 (因为你知道无效了嘛)

注意:volatile 不能保证原子性。

当执行一系列操作时时,不能完全代替synchornized 对线程的互斥。

instanceof

用于判断一个对象是否是某一个类。

IO

FILE

可以 new delete rename file or directory . 读写文件还是要用IO 流。

file 即可以是文件也可以是目录。

FileFilter

文件过滤器接口。需要实现 accept方法。

IO 流分类

分类 不同:

方向:in and out

类型:字节 字符(Reader Writer)

角色:

节点流:特定IO 设备。下层,低级流

处理流:对已存在的流进行封装。

参见流的类关系图

Java整理笔记基础 IO 面向对象 多线程 容器

https://docs.oracle.com/javase/7/docs/api/java/io/package-tree.html

Java整理笔记基础 IO 面向对象 多线程 容器

byte[] buf =new byte[1024];
while((hasRead=in.read(buf))>0)
序列化 ,反序列化

将对象保存到文件中。或者IO 流中传输

实现Serializable 接口。这只是一个标记接口(标记对象可以序列化),没有任何实现方法。

  1. 使用一个节点流,如 FileOutputStream 对象 构建一个处理流ObjectOutputStream对象。
  2. 调用ObjectoutputStream 对象中的 writeObject 方法 将 对象object序列化,并输出到objectOutputStream对象指定的流中。
oos=new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(a);

反序列化 则刚好相反,

  1. 节点流 ,构建一个ObjectInputStream
  2. readObject()

反序列化操作时必须提供该Java 对象所属类的class文件,否则将会抛出classnofound exception

外部序列化机制:

public interface Extenalizable  extends Serializable{
    
}

序列化版本:

因为 类可能随升级而发生变化 。a.txt却没有改变。旧的。

所以不对应。

面向对象

学会抽象,学会面向对象,学会面向百度。。。

面向对象与面向过程的区别:

面向过程是结构化的,以过程为中心。自顶向下。

面向对象是以面向对象为中心的,客观思维。

super 访问父类的。

特性:

java 不支持多重继承。

不同包 继承的权限问题。

同名变量和方法会覆盖

overload override

重载:多态

重写:

构造方法

初始化:

因为默认的是值 是 :数值类的:0

boolean: false

类名一致

不能有返回类型

可以重载 。

super 调用 父类的构造方法,得写在第一行。

只要定义了构造函数,原先默认的就不再提供了。

静态块是比构造函数还要先的。

抽象类和接口

抽象类不能被实例化。

抽象类不能有方法体。只是一个定义。

接口 中不能包含构造方法和初始化块,包含属性的只能 是常量,

不能final 修饰,因为抽象类就是用来继承的,final就不能继承了。

内部 类

内部类

位置来分类:

成员内部类

局部内部类

匿名内部类

  1. 成员内部类

    又分两种:静态 与非静态

    非静态是常用 的。

    在编译后会产生 两个文件 xxx $ InnerClassName.java

    在非静态成员内部类的方法中访问某个变量,该变量查找顺序是:

    局部变量-》内部类变量-》内部类所在的外部类变量

    静态:是类相关的内部类,它属于整个外部类,不属于外部类的某个对象。

    静态成员 内部 类,不能访问外部类实例化的。自身的非static 方法也不能访问。

  2. 局部内部 类

    如果把内部类定义在方法内,仅在方法内有效,不能访问修饰,不能static

  3. 匿名内部 类

    没有显式定义名字。

    适用于只使用一次的类。

多线程

线程与进程

进程是系统中系统进行资源分配和调试的独立单位

线程是进程组成部分。一个线程只能有一个父进程。

线程可以拥有自己的堆栈、自己的程序 计数器及自己的局部变量,但是线程不能拥有系统资源,它与其父进程 的其他进程共享进程 中的全部资源 。

当进程 被创建时,主线程就会被创建。所以至少会有一个线程。

Java 中Thread 表示线程的意思。

特点:

  1. 同一进程下的不同线程的调试不由程序 控制 。

  2. 线程独享 自己的堆栈程序 计数器 和局部变量。

  3. 并发执行。

java 中实现线程有几种方法:

一 : implements Runnable;
二 : 继承 Thread 或者 new Thread ;
比较推荐 Runnable  因为java 不支持多继承。如果 继承了 thread类就没有办法继承其它类了。

还有一种方法是 实现 Callable 接口来构建一个线程。

Thread.start()方法使该线程处于就绪状态。

Callable 比Runnable 更强大
// Runnable 没有返回结果,而Callable可以有返回结果。
//从定义中
    pulic interface Callable<V>{
//这是一个泛型接口
    V call() throws Exception;//相当run
}
V 指定了返回值类型。没有返回值就会异常。
    //如何获得 call()的返回值 呢?
    使用Futrue 接口来获得。
    Futrue 接口定义了一系列对Runable Callable 任务的执行结果任务进行查询,取消,获取结果。设置结果等。get方法获取返回值 
    会发生阻塞,直到call()返回结果。
    FutureTask<String>(Callable)
    new Thread( FutureaTask).start();
xx.get();//阻塞。

线程的状态与控制

Java整理笔记基础 IO 面向对象 多线程 容器

new -> call start()

得到cpU 就是run状态

阻塞状态 :

  1. 当线程调用 sleep join 就会阻塞。
  2. 阻塞式 IO
  3. 线程试图获取同步监视器。但被其它线程拥有。
  4. 在等待某个nofity通知
  5. 程序 调用了suspend 方法将线程挂起。

时间片用完 -》失去cpu

死亡状态 :异常 /正常结束/调用stop()方法

控制
  1. join方法

    ​ A调用 B 线程join方法,B 执行完了才继续A

    可以指定 时长join(long millis )毫秒。
        join (long millis  ,int nanos 微秒 )
    
  2. sleep

    A.sleep(long millis);阻塞

  3. yield方法

    A.yield();//暂时放弃CPU ,但不会被阻塞。而是进入就绪状态。
    
    
  4. 优先级

    priority 为1-10。10 最高


区别

sleep与 wait方法

sleep是静态方法。它是Thread的一个静态方法,其作用是使用运行中的线程暂时停止。

wait 是Object 类的方法,它是用来 实现 线程同步的。当某个对象的当调用 某个对象的wait方法后,当前 线程会被阻塞 并释放同步锁,直到其他 线程调用 了该 对象的notify 方法或者 notifyAll 方法来唤醒谇线程,所以wait 方法和notifyall notify 应当成对出现 以保证线程间的协调运行。???who

yeild 只是让出cpu ,让给优先级相同或更高的。


java中为什么不建议使用 stop suspend

线程不安全。会出ThreadDeath 异常。run的终止点不可估计。同时还会释放该线程的全部锁 这时可能数据不一致性。因为锁被释放,其它线程 进入临界区读到了破坏的数据,出现 不可预知的错误 。

suspend 阻塞。仍然持有线程锁。如果唤醒它的线程要用到它的资源(被锁),就死锁了。

最好使用wait 方法。???为什么?

那么 如何终止一个线程?

一:

使用退出标志。

设置一个boolean 值 判断

二:

使用interrupt 方法

抛出 中断异常。

class thr{
    try{
        xxx 
    }catch(InterruptedException)
    {
        然后处理一下
    }
}

class{
    main(){
        thread.sleep(1000);//得是阻塞中的线程才行。
        thread.interrput();//会让线程thr 出现 InterruptedException,得以执行 catch代码。
    }
}

线程同步

临界资源。

方法一:

同步代码块:

synchronized(obj){// 引用类型的对象。是一个同步监视器。
    //代码块
}
要想执行代码,先得得到 obj 同步监视器的锁定。

方法二:

synchronized void xxxfun(){//修改某个方法
与上面不同的是,不需要显式地指定同步监视器,默认是this ,也就是对象本身。
}  

方法三:同步锁。

对临界资源进行保护时,要定义一个Lock 对象。通常使用Lock的子类
ReentrantLock(可重入锁)的对象来进入加 放 锁。
private final ReentrantLock lock= new ReentrantLock();//final 很重要
lock.lock();//加锁
try{
    访问临界资源 ,需要保证线程安全代码。
}
finally {
lock.unlock();
}
比较建议 try...finally ... 来确保 同步锁一定会释放。


synchronized 和 Lock 的区别

synchronized 使用的是object 对象,即 同步监视器的notify 、wait、 notify All 调度机制来保证线程同步和协调执行。

而 Lock ,系统提供了Condition 类来保持 协调。不存在同步监视器等。

性能不同。

在竞争不激烈下:使用synchronized 优于 Locck

反之,则 低于

lock 还有可中断锁,超时失效锁。灵活一点。


wait()/notify()是object 的两个方法,必须由同步监视器对象来调用 。调用wait()方法会让当前线程暂停。阻塞。并释放同步监视器。当其他线程调用 该同步监视器的notify() ,阻塞在该同步监视器上的线程的会被唤起。notify()只唤一个

await() 和signal 与上面两个差不多,是jdk5.0后的。更好吧。

blockingqueue 阻塞队列方法。它内部实现了同步队列。新建对象 put():添加,容量到max时,自动 阻塞?

take():删除,当容量为0时,自动 阻塞。?

pipedInputStream 和 pipedOutputStream :用于进程 之间的 交互 。

守护进程

线程类型:用户进程 和守护进程 :Daemon

后台和服务。如垃圾回收装置。

调用 setDaemon(true)方法 即可设置。

java 容器

太多了,百度class hierarchy 吧

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d680a2aX-1600957173412)(https://facingissuesonitcom.files.wordpress.com/2019/07/java-collection-framework-hierarchy.jpg?w=1920)]

Java整理笔记基础 IO 面向对象 多线程 容器

collection 接口

是 根接口之一。

定义:单一元素的数据结构的接口

有3 个子接口。

  1. List :有序的,可重复的。
  2. Set: 无序的、不可重复的。
  3. Queue:队列集合,特殊的线性表,FIFO

Iterator 接口

它可以在不知道容器中元素类型的情况 下遍历容器中元素。

concurrentModificationException:快速失效机制fail-fast

Arraylist 从父类AbstractList继承了属性值modCount, 用于记录被修改的次数。

与此同时,父类AbstractList也实现了iterator()方法,调用该方法会返回一个实现 了Iterator 接口的内部 类Itr的实例。

在该类Itr中有一个属性expectedModCound 初始化为 modCount 的值 ,在类Itr 中执行 next()或remove()时,都会检测 两个是否相等。而Itr提供的remove方法会在删除元素后会同步修改expectdModCount的值 。所以迭代器的remove()方法是安全的。


Collection 与Collections 的区别

Collections是容器框架 的工具类。静态方法提供 操作。直接类名调用 。

Collection 接口定义这些容器类操作容器元素的基本方法。

HashSet 和 TreeSet

Java整理笔记基础 IO 面向对象 多线程 容器

java.util

关于hashset :

  1. hashset 中的元素可以是null
  2. 非线程安全的。
  3. 不保证与插入顺序一致。
  4. 如果 有重复对象,则不添加 。返回false

hashset 内部封装了hashmap 。当add() 实际上是用hashmap的put()。

重复对象是通过 hashcode()和equals这两个共同方法确定的。

hashSet 中封装的 hashmap 的put()的源码中:

实际上向hashset 添加的是hashmap 中的key ,而不是value

代码首先,得到key的hash值,然后得到key 在hashmap中的索引 。

 /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

因为hashmap 本质上是一个数组+链表+红黑树的数据结构。

关于TrreeSet:

  1. 有序的集合。
  2. 非线程安全
  3. 重复对象,则不添加 。

ArrayList 、Vector、LinkedLis

ArrayList

ArrayList 又称为动态数组

内部 数据 结构由数组 实现,因此可对容器内元素实现 快速 随机访问。但插入和删除就要移动 其他 元素,所以不适合在插入和删除操作频繁 的场景下使用ArrayList.

与此同时,ArrayList的容量可以随着元素的增长而自动 增加。每次扩容为1.5倍。

jdk1.8之前 默认容量为10,之后 为空的object数组,add第一个元素则初始化为10

两个成员 变量。

它是非线程安全的。

Vector

向量类,也实现 类似动态数组的功能有。内部结构也使用数组实现。与arraylist 不同的是 它是线程安全的它的都是同步方法

是非泛型集合,也就是可以随意插入 不同类的对象。

有3个成员 属性

    protected Object[] elementData;

    /**
     * The number of valid components in this {@code Vector} object.
     * Components {@code elementData[0]} through
     * {@code elementData[elementCount-1]} are the actual items.
     *
     * @serial
     */
    protected int elementCount;

    /**
     * The amount by which the capacity of the vector is automatically
     * incremented when its size becomes greater than its capacity.  If
     * the capacity increment is less than or equal to zero, the capacity
     * of the vector is doubled each time it needs to grow.
     *
     * @serial
     */
    protected int capacityIncrement;

性能?

LinkedList

实现 了Deque接口。可以双端队列使用。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    
}

因为链式结构插入删除效率高。

HashMap 和Hashtable

HashMap

数组+链表+红黑树 来实现

HashMap 底层是一个数组 Entry[ ] table , 数组中的每个元素Entry 都是一个单项链表的引用,从jdk1.8开始,当链表大于8时,链表会高速为红黑 树结构。

初始容量和加载因子。超过两个的乘积时,扩容后数组大小当前 的2倍。

Hashtable

是线程安全的,

推荐ConcurrentHashMap 代替,因为性能优势大


HashMap 为什么引入红黑树结构?

HashMap 采用数组 和链表相结合的数据结构,底层是一个数组,第个数组元素都 是一个链表结构,链表的每个结点就是HashMap中的每个元素(键值对)。当添加一个键值对时,会先调用该键值对的key的hashcode()方法计算出hashcode值,从面得该元素在数组中的下标。如果 数组在该位置上已保存有元素(已存在链表),则说明 发生了冲突(不同的key 对就了同一个hash值 ,所以映射的下标 也相同。),接下来要用冲突管理算法进行处理。

HashMap 采用链地址法,即用单链表将所有冲突的元素链接起来。但是这个链表并不会无限地增长,当链表中元素个数大于8时,这个链表会算卦以转为红黑树。

之所以引入 红黑树是因为在链表中查找每个元素的时间复杂度都是O(n),而在红黑树中查找元素的时间复杂度为O(log n),这样当HashMap中元素较多并产生了大量Hash冲突时,红黑树的快速增删改查能提高HashMap性能 。

为什么小于8个的时候不用?

因为小于的时候,用链表更快,。红黑 复杂。

红黑树 是 自平衡二叉查找树

用黑色和红色标记结点。有三个特点:

  1. 根与叶子结点都 是黑色的。
  2. 从每个叶子 到根的所有路径不能有两个连续的红色结点。
  3. 从任一结点 到它所能到达的叶子结点的所有简单路径都 包含相同数目的黑色结点。

以上三个特性保证了了比其他二叉查找树有更好的结点 查找稳定性,查找效率和增删效率。

综上原因。…