Java学习笔记

常见问题

[TOC]


MySql password: dg0zt7vh3,&N

  • cmd/powershell乱码

切换字符集:chcp 65001(UTF-8)、chcp 936(GBK)

  • 从C盘切换到D盘

输入:d:

  • 网络相关

网关地址就是路由器的IP地址,静态IP需要设置默认网关、DNS、子网掩码等等,动态IP只需要路由器DHCP自动分配IP地址

位运算

  • 首先要强调一点:计算机处理数据都是以补码的形式进行计算

1. 取反 ~

~ 把补码中的 0 和 1 全部取反(0 变为 1,1 变为 0),取反时每一位都参与运算,包括符号位

  • 计算:~9~-9
~9 的计算步骤:

原码:0 1001

补码:0 1001

按位取反:1 0110

原码:1 1010

 = -10


 ~-9 的计算步骤:

原码:1 1001

补码:1 0111

按位取反:0 1000

原码:0 1000

 = 8

2. 移位 >>

右移时需要在左侧用符号位(正数为 0,负数为 1)补齐

  • 负数的右移

解释:-5 >> 2 == -2

-5

原码 1000 …… 0101

反码 1111 …… 1010 负数的反码是保留符号位不变原码取反

补码 1111 …… 1011 补码是反码加1

>>2 (负数右移高位补1)

补码 1111 …… 1110

反码 1111 …… 1101 补码转反码减1

原码 1000 … 0010 负数反码转原码保留符号位不变取反

= -2
  • 正数的右移

解释:5 >> 2 == 1

+5

原码 0000 …… 0101

补码 0000 …… 0101

>>2(正数右移高位补0)

补码 0000 …… 0001

原码 0000 …… 0001

= 1
  • 左移

左移时在后面补 0。值得注意的是,对于 int 型来说左移 32 位等于原来的数,移位的位数 >= 32 时会先取余再进行移位,比如左移 34 位等于左移 2(34 % 32)位。

相对于右移,左移还需要考虑溢出的问题。

异或 ^

  • 异或满足的性质
    • 归零率:a ^ a = 0
    • 恒等率:a ^ 0 = a
    • 交换律:a ^ b = b ^ a
    • 结合律:a ^ (b ^ c) = (a ^ b) ^ c

由异或的归零率和结合律可以推出异或的另一条性质:

自反: a ^ b ^ b = a ^ 0 = a

  • 异或的应用
    • (1) 快速比较两个值是否相等(归零率)

利用a ^ b == 0可以快速判断 a 和 b 是否相等

交换两个变量的值

a = a ^ b;

b = a ^ b;     //a ^ b ^ b = a ^ 0 = a

a = a ^ b;     //a ^ b ^ a = b ^ a ^ a = b
  • 最常出现的面试题:一个整型数组里除了N个数字之外,其他的数字都出现了两次,找出这N个数字;

比如,从 {1, 2, 3, 4, 5, 3, 2, 4, 5} 中找出单个的数字: 1

首先,我们来看一个简单的例子。因为异或满足交换律、结合律和归零率,所以假设有数组:A B C B C D A 使用异或:

  A ^ B ^ C ^ B ^ C ^ D ^ A

= A ^ A ^ B ^ B ^ C ^ C ^ D

= 0 ^ 0 ^ 0 ^ D

= 0 ^ D

= D

这样我们就找出了只出现一次的元素:D

代码

//空间复杂度为O(1),时间复杂度为O(n)

int singlenumber(int *array*[], int *length*){

  if(length <= 0)

​    return -1;

  if(length == 1)

​    return array[1];

  int result = 0; // a ^ 0 = a

  for(int index = 0; index < length; index++){

​    result = result ^ array[index];

  }
    findonebit(int num)

  return result;

}

模运算

  • a % b 的步骤:

1.求整数商:c = a / b

2.计算模或者余数:r = a - c * b

如果 a, b 都为正数,则取模与取余得到的商相同

对于被除数为负数的取模运算结果取决于具体的编程语言,一般分为两种类型:

1.floor除法,就是商采用floor法取整,因而也叫趋负无穷截尾。目前采用这种方式的编程语言有python等。

例如:-7 % 3 中,商向下取整得 -3,所以模就为 2.

2.truncate 除法,就是商尽可能的靠近 0,因此又称截断取整。目前采用这种的方式的编程语言有Cjava还有js等。例如-7 % 3,靠 0 取整得 -2,所以模就为 -1.

巧记:floor法的话,模的符号和除数相同;而truncate法的话,模的符号和被除数相同。


继承

  • 构造方法的继承

    • 父类有无参构造器,子类才可以写无参构造器;父类有含参构造器,子类才可以写含参构造器
    • 构造器不能被继承、重写
    • 当进行无参构造时,先调用父类无参构造器,然后调用子类无参构造器;当进行含参构造时,先调用父类含参构造器,然后调用子类含参构造器。
  • superthis 关键字

  • 动态绑定和静态绑定

    • 动态绑定:根据具体对象的类型进行绑定,发生在运行阶段,绑定的是对象信息

    • 静态绑定:编译过程中就已经知道这个方法到底是哪个类中的方法。发生在编译阶段,绑定的是类信息,即为定义的类的类型

      java当中的方法,只有finalstaticprivate重载方法(overloaded methods)构造方法是静态绑定。所有的变量都是静态绑定。

特别说明的一点是,private声明的方法和成员变量不能被子类继承,所有的private方法都被隐式的指定为final的,所以是静态绑定的。(由此我们也可以知道:将方法声明为final类型的一是为了防止方法被覆盖,二是为了有效的关闭java中的动态绑定)

多态

多态分为编译时多态运行时多态

实现多态的三个条件: 1.继承 2.重写 3.父类引用指向子类对象

抽象类和接口

抽象类: Abstract class

  • 有抽象方法的类只能定义为抽象类;只要包含一个抽象方法的抽象类,该类必须要定义为抽象类,不管是否还包含其他方法;
  • 抽象类中可以包含普通方法(有具体实现的方法),当然也可以不包含抽象方法;
  • 抽象类不能被实例化,即不能用new来实例化抽象类;
  • 抽象类可以包含属性、方法、构造方法。但是构造方法不能用new来实例,只能用来被子类调用;
  • 抽象类只能用来被继承;
  • 抽象方法必须被子类实现,即由子类来进行重写。(不实现的话,编译器会报错)

接口: Interface

  • 子类通过implements来实现接口中的规范;
  • 接口不能创建实例,但是可以用于声明引用变量类型;
  • 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public
  • 接口支持多继承。
  • 接口和接口之间可以相互继承,类和类之间可以相互继承,类和接口之间,只能是类来实现接口。

接口与抽象类的区别

  • 抽象类表示的是一种继承关系,一个子类只能继承自一个抽象类。但是,一个类却可以实现多个接口
  • 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量默认全都是 public static final 类型的
  • 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法

内部类

  • 静态内部类
  • 成员内部类
  • 方法内部类
  • 匿名内部类

常用类

Object类

getClass()、hashCode()、equals()、clone()、toString()

==equals 方法的异同

  • 相同点:对于基本类型来说两者没有区别,都是比较值是否相同

  • 不同点:

    • == 比较引用类型时是在比较地址值是否相同
    • equals方法本质上就是 ==,只不过 StringInteger 重写了 equals 方法,把它变成了对值的比较
  • clone()方法

clone方法可分为浅克隆深克隆

  • 浅克隆,即很表层的克隆,如果我们要克隆对象,只克隆它自身以及它所包含的所有对象的引用地址
  • 深克隆,克隆除自身对象以外的所有对象,包括自身所包含的所有对象实例

要完成深度拷贝就必须实现 Clonable 接口,并重写 clone 方法

实现深克隆和浅克隆的最关键的就是要实现 Object 中的 clone() 方法

public class Test {
    static class Body implements Cloneable {
        public Head head;

        public Body() {
        }

        public Body(Head head) {
            this.head = head;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            Body newBody = (Body) super.clone();
            newBody.head = (Head) head.clone();
            return newBody;
        }
    }

    static class Head implements Cloneable {
        public Face face;

        public Head() {
        }

        public Head(Face face) {
            this.face = face;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    static class Face implements Cloneable {
        public Face() {
        }
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head());
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
    }
}

String类

创建字符串对象时,首先会去常量池里找有没有这个字符串,有的话就直接指向该字符串,没有的话就先往常量池中添加一个,再指向它。

String str1 = new String("Hello");
String str2 = "Hello";
System.out.println(str1.equals(str2));//true
System.out.println(str1 == str2);//false
  • new一个字符串时,做了两件事。首先在堆中开辟相应的内存空间,将内存空间的引用赋值给str1,然后去看常量池中有没有该字符串,如果有就不管了,没有就往常量池中加一个,并将其内存地址存入堆中new出来的空间中。

  • 用字面量赋值创建,首先会去常量池中找有没有这个字符串,有就直接指向常量池的该字符串,没有就先往常量池中添加一个,再指向它。

  • trim() 和 compareTo():

public class StringTest {
    public static void main(String[] args) {
        String str1 = "  Hello Worlda ";
        String str2 = "  Hello Worldb";

        String trim = str1.trim();
        System.out.println(trim);  //trim方法只去除字符串头和尾的空格
        System.out.println(str1.compareTo(str2)); //compareTo()方法返回两个字符串第一个不相等字符ASCII码相减的值
    }
}

//结果:
Hello Worlda
-1
  • byte[ ] getBytes()

    • getBytes(String charsetName) 对字符串按照 charsetName 进行编码(unicode→charsetName),返回编码后的字节。 getBytes() 表示按照系统默认编码方式进行。

    • String(byte bytes[], Charset charset) 对字节按照 charset 进行解码(charset→unicode),返回解码后的字符串。 String(byte bytes[]) 表示按照系统默认编码方式进行

public class StringTest {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "浣犲ソ"; //这是"你好"的gbk编码的字符串
        String str = "abcd";
        byte[] b = str.getBytes();
        System.out.println(Arrays.toString(b));
        String ss = new String(s.getBytes("GBK"), "UTF-8");
        System.out.println(ss);
    }
}
//运行结果:
[97, 98, 99, 100]
你好

String、StringBulider 和 StringBuffer

Data

异常

Throwable
 |--Error
 |--Exception
  |--RuntimeException 运行时异常
  |--非RuntimeExcetion 编译时异常,必须处理,否则编译不通过

Throwable基本方法:

  • toString()
  • getMessage()
  • printStackTrace()

IO

File

  • 获取当前路径:

System.getProperty("user.dir"); //use.dir指定了当前路径

  • 创建一个新文件:
public File(String pathname)
如果 pathname 为 null,则抛出 NullPointerException

public File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
如果 parent 为 null,则创建一个新的 File 实例,这与调用以给定 child 路径名字符串作为参数的单参数 File 构造方法效果一样。 
否则,parent 路径名字符串用于表示目录,child 路径名字符串用于表示目录或文件。
如果 child 路径名字符串是绝对路径名,则用与系统有关的方式将它转换为一个相对路径名。 
如果 parent 是空字符串,则通过将 child 转换为抽象路径名,并根据与系统有关的默认目录解析结果来创建新的 File 实例。
否则,将每个路径名字符串转换为一个抽象路径名,并根据父抽象路径名解析子抽象路径名。

参数:
parent - 父路径名字符串
child - 子路径名字符串
抛出:
NullPointerException - 如果 child 为 null

public File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
如果 parent 为 null,则创建一个新的 File 实例,这与调用给定 child 路径名字符串的单参数 File 构造方法的效果一样。
否则,parent 抽象路径名用于表示目录,child 路径名字符串用于表示目录或文件。
如果 child 路径名字符串是绝对路径名,则用与系统有关的方式将它转换为一个相对路径名。
如果 parent 是空抽象路径名,则通过将 child 转换为抽象路径名,并根据与系统有关的默认目录解析结果来创建新的 File 实例。
否则,将每个路径名字符串转换为一个抽象路径名,并根据父抽象路径名解析子抽象路径名。

参数:
parent - 父抽象路径名
child - 子路径名字符串
抛出:
NullPointerException - 如果 child 为 null

//例子:下面三种构造方法效果一样
File file1 = new File("D:\\Chrome\\new.dat");
File file2 = new File("D:\\Chrome", "new.dat");
File file3 = new File("D:\\");
File file4 = new File(file3, "new.dat");
  • 创建一个文件夹:
    • mkdir() 方法创建一个文件夹,成功则返回 true,失败则返回 false
    • mkdirs() 方法创建一个文件夹和它的所有父文件夹
String dir = "D:\\Chrome\\java";
File d = new File(dir);
d.mkdir();
  • 递归的删除一个目录下的所有文件
public class Demo {
    public static void main(String[] args) {
        System.out.println(System.getProperty("user.dir"));
        File dir = new File("D:\\DeleteTest");
        deleteDir(dir);
    }
    //一定要分清是对象的方法还是方法的参数
    public static void deleteDir(File dir){
        File[] files = dir.listFiles();
        for(File file : files){
          //是目录就递归删除
            if(file.isDirectory()){
                deleteDir(file);
            }
            //是文件就直接删除
            else
                file.delete();
        }
        //删除空目录
        dir.delete();
    }
}

IO 流

InputSteam 和 OutputSteam

  • FileInputSteam
read
public int read()
         throws IOException从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞。

指定者:
类 InputStream 中的 read
返回:
下一个数据字节;如果已到达文件末尾,则返回 -1。
抛出:
IOException - 如果发生 I/O 错误。

关于 read() 的返回值:
read() 返回一个 unsigned byte [0 - 255],而 java 里面没有这个类型,所以用 int 接收。byte 的范围是[-128,127],所以如果 read() 返回的数在[128,255]的范围内时,则表示负数。所以如果 read() 返回的是 byte 的话,那就会有负数。而"返回-1意味着结束",这个信息量用 byte 是无法表达的,所以必须用 int

实例:将文本文件的内容打印到屏幕上


FileReader in = new FileReader("D:\\chrome\\java.dat");
int num = 0;
while((num = in.read()) != -1){
   System.out.print((char)num);
}

//使用字节数组
FileInputSteam in = new FileInputSteam("D:\\chrome\\java.dat");
int cnt = 0;
byte[] b = new byte[1024];
while((cnt = in.read(b)) != -1);
for(byte by : b){
    System.out.print((char)by)
}

--------------------------------------------------------------------------------

read
public int read(byte[] b)
         throws IOException从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。在某些输入可用之前,此方法将阻塞。

覆盖:
类 InputStream 中的 read
参数:
b - 存储读取数据的缓冲区。
返回:
读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
抛出:
IOException - 如果发生 I/O 错误。
另请参见:
InputStream.read(byte[], int, int)

注意:这两个 read() 方法的返回值不同,  但共同点是在遇到文件尾(end-of-file)时返回 -1,
  • FileOutputSteam
write
public void write(int b)
           throws IOException将指定字节写入此文件输出流。实现 OutputStream 的 write 方法。

指定者:
类 OutputStream 中的 write
参数:
b - 要写入的字节。
抛出:
IOException - 如果发生 I/O 错误。

qwertyuiopasdfghjklzxcvbnm
--------------------------------------------------------------------------------

write
public void write(byte[] b)
           throws IOException将 b.length 个字节从指定 byte 数组写入此文件输出流中。

覆盖:
类 OutputStream 中的 write
参数:
b - 数据。
抛出:
IOException - 如果发生 I/O 错误。
另请参见:
OutputStream.write(byte[], int, int)
  • FileReader 和 FileWriter

    • FileReader、FileWriter的构造方法和read()、write()方法与FileInputSteam、FileOutputSteam基本一致,但是FileReader 和 FileWriter是面向字节流的需要使用 char[] 数组
    • FileWriter 的 write() 方法需要 flush()
read
public int read()
         throws IOException读取单个字符。

覆盖:
类 Reader 中的 read
返回:
读取的字符,如果已到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误

--------------------------------------------------------------------------------

read
public int read(char[] cbuf,
                int offset,
                int length)
         throws IOException将字符读入数组中的某一部分。

指定者:
类 Reader 中的 read
参数:
cbuf - 目标缓冲区
offset - 从其处开始存储字符的偏移量
length - 要读取的最大字符数
返回:
读取的字符数,如果已到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误

Thread

Thread 类 和 Runnable 接口

创建一个线程的两种方法:

  • 直接从 Thread 类继承,就可以创建一个线程实例
    • Thread 的构造方法
Thread()
Thread(String name)
Thread(Runnable target)
Thread(Runnable target, String name)
  • 可以实现 Runnable 接口,并重写 Runnablerun() 方法, 不过实现 Runnable 后,还是要通过一个 Thread 来启动:
public class day23_01 {
    public static void main(String[] args) {
        Runner r = new Runner();
        //r.start();//继承自Thread的时候可以直接start()
        Thread thread = new Thread(r);
        thread.start();
        for(int index = 0; index < 1000; index++){
            System.out.println("main thread: "+index);
        }
    }
}
class Runner implements Runnable{
   @Override
   public void run() {
       for(int i = 0; i < 1000; i++){
           System.out.println("runner thread: "+i);
       }
   }
}
// class Runner extends Thread{
//     @Override
//     public void run(){
//       for(int i = 0; i < 100; i++){
//         System.out.println(i);
//       }
//     }
// }

线程状态

  • 新建状态 New
    • 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该对象就处于新建状态。它保持这个状态直到 start()
  • 就绪状态 Runnable
    • 当线程对象调用了 start() 方法之后,该线程就进入就绪状态
  • 运行状态 Running
    • 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程就处于运行状态。处于运行状态的线程可以变为阻塞状态、就绪状态和终止状态
  • 阻塞状态 Blocked
    • 如果一个线程执行了 sleep()suspend() 等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获取设备资源后可以重新进入就绪状态。
  • 终止状态 T
    • 线程执行完了或者因异常退出了 run() 方法,该线程结束生命周期

方法

Daemon() 必须在 start() 之前,join() 要在 start() 之后 当 jvm 中运行的线程全是守护线程时,jvm 退出

线程同步

Synchronized

JSRUN前端笔记, 是针对前端工程师开放的一个笔记分享平台,是前端工程师记录重点、分享经验的一个笔记本。JSRUN前端采用的 MarkDown 语法 (极客专用语法), 这里属于IT工程师。