Java基础知识整理(连续更新中)

目录

1、进制之间的转换:

2、十进制小数转二进制小数:

3、为什么计算机中小数是不精准的?

4、计算机中的单位

5、取值范围  

6、为什么4个字节的浮点型取值范围大于8个字节的整型;

7、原码、反码、补码:

8、java的基础知识

9、关键字和标识符:

10、常量的分类

11、变量

12、java中的数据类型

13、java中的运算符

15、循环

16、方法(method)

17、数组

18、地址值

19、面向对象

     1、类:

    2、对象

   3、类的定义:

    4、对象的创建语法:

    5、对象在内存中的位置:

    6、使用自定义类型作为方法的参数

    7、使用自定义类型作为方法的返回值

    8、成员变量和局部变量的区别

20、 this关键字

21、构造方法

22、面向对象三大特性:封装,继承,多态;

     1、封装:

     2、继承:

     3、多态 

23、如何定义一个标准的类

24、常用类

     1、ArrayList 数组集合类

     2、Math类

     3、一个类就是一种数据类型;

     4、泛型(未知的数据类型)E e;

     5、String类:

     6、static静态修饰符可以修饰:

     7、System类:

25、接口

26、final关键字

27、权限修饰符(Y:代表可以访问到                 N:代表不能访问到)

28、内部类

    1、成员内部类

    2、局部内部类

    3、匿名内部内

29、StringBuilder:

30、Date和Calender,SimpleDateFormate

    1、Date 

    2、simpleDateFormate

    3、Calendar

31、Object类(顶级父类)

32、==和equals比较的是什么:

33、常量与常量的拼接结果在常量池,原理是编译器优化。

34、集合

     1、单列集合:Collection接口

     2、 双列集合:Map接口

     3、Iterator接口:

     4、增强for循环(forEach)

35、泛型:未知的数据类型

     1、含有泛型的类:

      2、实现含有泛型的接口:

      3、含有泛型的方法:

      4、泛型的通配符:

      5、泛型的限定:

  36、Collection

37、List接口

38、Set接口:

 39、hashCode()和equals():

40、两种比较器的区别与联系

41、Collections类(集合工具类)

42、Map接口 (双列集合)  

43、数据结构

44、HashSet的源码分析

45、Throwable类(异常类)

46、进程与线程:

    1、进程:一个软件就是一个进程,一个软件可以有多个进程

    2、线程:一个软件可以有一个/多个线程;

    3、并行和并发:

    4、调度方式:

    5、Thread类:

    6、多线程:就是创建一个Thread类型的对象,启动执行。

    7、创建多线程的方式:

        1、继承Thread类:

        2、实现Runnable接口,传入Runnable类型的target(使用有参构造方法)

   8、多线程的安全性:

    9、synchronized与Lock锁的对比?

   10、线程的状态:

   11、栈,本地栈,程序计数器是线程独有的;

47、线程间的通信:

    1、 等待唤醒机制:

    2、Object类:

48、lambda表达式:

49、死锁:

50、线程池(ThreadPoolExecutor)

    1、线程池的优点:

    2、线程池构造方法的参数:

    3、线程池执行任务的流程步骤:

    4、Executors类:

    5、ExecutorService接口:

    6、Future接口

    7、ScheduledExecutorService接口:

51、File类:

52、深浅拷贝:

53、IO(输入输出)

      1、InputStrem和OutputStrem(字节输入,输出流)

      2、字符输出流:Writer

      3、处理IO异常注意的点(资源需关闭):

     4、缓冲流

      5、转换流

      6、对象流

      7、字符编码和字符集:

54、网络编程: 

55、常用函数式接口:

56、枚举(ENUM)


1、进制之间的转换:

     十进制转其t进制:将十进制的值除以t,得到商和余数,商接着除以t,除到商为0为止,余数倒着看,就是十进制转为t进制的结果;

     t进制转十进制:位权法,二进制数值从右向左以此对应t的次方,从0次方开始,上下对齐相乘,再前后相加,得到的和就是对应的十进制的值;

     十六进与二进制的转换:

        十六进制 -> 二进制:

           将一位十六进制转为四位二进制,不够前面补0;

        二进制 -> 十六进制:

           从右向左,每四位二进制转为一位十六进制;


2、十进制小数转二进制小数:

     整数部分转整数部分;

     小数部分转小数部分;

        小数部分:值成以二,如果值大于1,则在小数位补1,值小于1则补0,重复这个过程,直到结果为0或者循环为止,如果是循环,则结果保留八位,

   十六进制和二进制小数的转换:整数部分转整数,小数部分转小数    

        十六进制 ->  二进制:

            小数部分:一位十六进制转为四位二进制,不够前面补0;

        二进制 ->  十六进制:

            小数部分:从小数部分以2的-1次方开始,然后上下相乘,再前后相加得到的值就是十六进制小数的值;


3、为什么计算机中小数是不精准的?

   就是由于十进制小数转二进制小数的过程中,二进制小数会出现循环,大部分十进制小数如0.1、0.2、0.3转成二进制小数都会有循环,只有0.5转换过去是0.1为精准的,因此计算机中大部分小数是不精准的。


4、计算机中的单位

    最基本的单位 byte, 

    1byte = 8bit(位);
    1kb = 1024byte;
    1mb = 1024kb
    1gb = 1024mb
    ....


5、取值范围
  


   1byte的取值范围为:-2^7 ~ 2^7-1 (-128 ~ 127) 01111111 ~ 10000000;
   2byte的的取值范围 -2^15 ~ 2^15-1 (-32768 ~ 32767) 1(符号位) + 15个0 ~ 0(符号位) + 15个1;
   4byte的取值范围 -2^31 ~ 2^31 (-2147483648 ~ 2147483647)  1 + 63个0 ~ 0 + 63个1;
   8byte为 -2^63 ~ 2^63 -1;


6、为什么4个字节的浮点型取值范围大于8个字节的整型;

    4个字节的浮点型存储时的结构为
    1个符号位 + 8个指数位(一个符号位 + 7个数值位) + 23个尾数位 取值范围为 -2^127~2^127;
    8个字节的浮点型的结构为 1个符号位 + 11个指数位(1个符号位+10个数值位) + 52个尾数位 -2^1023 ~ 2^1023;

7、原码、反码、补码:


    
    有符号数和无符号数:

       有符号数:就是负数;

       无符号数:只有正数(包含0);

    正数:

       三码合一;

    负数:

      原码:十进制直接转为二进制(8位不够补0),加符号位 的就是原码;

      反码:原码中符号位不变,其余位取反(1变0,0变1);

      补码:反码加1;

   计算机中数据的存储,运算都是以补码的形式存在;

8、java的基础知识

      环境变量的配置 JAVA_HOME = "jdk所在的路径";path=%JAVA_HOME%\bin\
           
      javac Test.java   javac将java源文件编译成字节码文件;

      java Test(class)  java运行字节码文件;


9、关键字和标识符:

    关键字:有特殊含义的,被保留的(goto),不能随意使用的。

    public  class  static  void 等这些都是关键字。

    标识符:我们自己定义的类名、变量名、方法名
规则:
硬性要求:
1.字母、数字、下划线、$、中文都可以
2.但是数字不能开头
3.标识符不能是关键字
4.常用的就是纯字母,或者字母+数字

软性要求:
大驼峰:每个单词的首字母大写(类名)
小驼峰:第一个单词首字母小写,此后每个单词的首字母大写(变量名、方法名)。


10、常量的分类

 
     整型、浮点型、布尔型、字符型、字符串型;

     常量的值是不能被改变的;

11、变量

    变量名不能重复;

    变量必须赋值后才能使用;

12、java中的数据类型


   基本数据类型型和引用数据类型;

      基本数据类型:

        数值类型和非数值类型

          数值类型:

            整型和浮点型:

                整型(默认为int):
                byte(1字节)(-128~127);
                short(2字节)(-32768~32767);
                int(4字节)(-2147483648 ~ 2147483647);
                long(8字节)(-2^63 ~ 2^63 -1)如 long l = 122323445L ;

                浮点型(默认float):

                float(4字节)(-2^127~2^127)如 float f = 123.456F;
                double(8字节)(-2^1023 ~ 2^1023);

            非数值类型:

                char(2字节)(0~65535)、boolean(1字节)

     引用数据类型:(保存的是对象在内存中的地址)

          String、Date、Math、UUID等

    数据类型的转换

       自动类型转换和强制类型转换:

           自动类型转换:

               小范围类型向大范围类型变量转换;

               语法:

                大范围类型 变量名 = 小范围的值(常量/变量);

                char和int会互相进行自动类型转换;

                只要整型常量的值不超过byte、short、char的取值范围就完全可以存储;

                byte、short、char这些类型的变量来说,在做运算的时候,会自动被提升为int类型;

              

           强制类型转换:


               大范围向小范围类型的变量转换:

                  语法 小范围类型 变量名 = (小范围类型)大范围的值(常量/变量);

               会出现精度丢失和数据溢出的情况;


13、java中的运算符

   + :

     1、+两边都是数值型,则进行加法运算;

     2、+两边又一边是字符串,则进行的是拼接操作;

   - :减法运算

   * :乘法

   / : 除法

      如果两边中都是整型,则结果为整形,如果有一边是浮点型,则结果为浮点型

   = :赋值操作

   == :等于

   != :不等于

   !:取反

   % :模运算 取余

     如果两边都是整型,则结果为整数

     如果有一边是浮点型,则结果为浮点数 

   逻辑运算 && 和 || 

      && 逻辑与 符号两边的为布尔值 如果有一边为false,则整个结果就为false;如果两边都为true,则结果为true;(如果左边为false,则结果就直接为false,不再继续右边的判断)(短路功能的体现)

      || 逻辑或 符号两边的为布尔值 如果有一边为true,则整个结果就为true;如果两边都为false,则结果为false;(如果左边为true,则结果就直接为true,不再继续右边的判断)(短路功能的体现)

      !:取反

        右边为布尔值;

    三元运算符

       判断条件 ? 表达式1 : 表达式2

       一般写法: 表达式1和2的数据类型 变量名 =  判断条件 ? 表达式1 : 表达式2

    ++ -- (自增自减运算)

       单独运算:

         a++和++a是不区分的,都是先运算再使用;

       混合运算:

         ++a 是先运算再使用(赋值/输出)

         a++ 是先使用(赋值/输出)后运算


    +=:   a+=b 相当于  a = a+b;
         好处是有时候需要强制类型转换的时候,不需要我们自己去做,底层就会帮我们自动进行强制类型转换;

14、流程控制
   
   if语句
     格式:
       if(判断条件){
         判断条件为真时所要执行的
       }

       if(判断条件){
           判断条件为真时所要执行的
       }else{
           判断条件为假时所要执行的
       }

       if(判断条件1){
           判断条件1为真时所要执行的
       }else if(判断条件2){
           判断条件2为真时所要执行的
       }else{
          判断条件都不满足时所要执行的
       }
       //else if可以写多个

   注意事项:从语法上来说,if分支的大括号可以省略,如果省略,则if只包含紧邻的下一条语句。

    用于等值判断和区间判断


    switch语句

      格式:
        switch(表达式){
           case 常量1:
                   //对应的代码
                   break;
               ....
               case 常量n:
                   //对应的代码
                   break;
               default:
                   //前面判断都为false,要执行的代码
                   break;
        }

        注意事项:
               1. case后面的常量不能重复
               2. case后面的值必须是常量
               3. break结束switch结构
               4. 表达式的类型可以是 byte short int char String 枚举
               5. swicth会出现穿透现象,
                  穿透现象:      
                    就是当一个case里没有break;会一进入到下一个case中,并且不会触发case里对应的判断,知道有break的case为止;

                    当我们有好几个常量所对应的结果都相同时,可以使用switch的穿透特性来简化代码如:
                      switch(mounth){
                         case3:
                         case4:
                         case5:
                            System.out.println("春季");
                            break;
                      }

                switch的底层结构使用了跳转表(数组),也就是为什么switch里的常量的值不能超过int类型的取值范围,也就是常量里不能使用long、double、float等大于int取值范围的变量的原因;
               
       if分支:可以做等值判断,也可以做区间判断    

       switch:只能做等值判断,不能做区间判断。
       
          如果是等值判断,建议使用switch,效率高。

15、循环

    for(①初始化变量;②条件判断;④步进语句){
         ③循环体
    }

     //初始化变量可以是多个

    //死循环的写法

    for(;;){}

    2.while循环

     基本格式:
      while(条件判断为真){
        //循环体
      }

     扩展格式:
      ①初始化变量
      while(②条件判断){
         //③循环体
         ④步进语句
       }


    3.do while循环
      基本格式:
      do{
        //循环体
      }while(条件判断);

      扩展格式:
       ①初始化变量
       do{
        //循环体
        //步进语句     //循环体和步进语句的先后顺序因具体情况而定
        
        }while(④条件判断);
    //区别:
    do while:即使条件不满足,也会执行一次

    4.三种循环的区别
      语法区别:
    1.do while:即使条件不满足,也会执行一次。for while条件不满足一次都不执行

    2.for初始化变量只能在所属范围内使用(大括号),

     do while 和while能再所有范围内使用。


   5.循环控制break语句

      作用:终止当前循环

   6.循环控制continue语句

      作用:跳过本次循环,继续下次循环

   7.循环嵌套

      外层循环走一遍,内层循环走一圈

      外层循环控制行数,内层循环控制列数


16、方法(method)

   
   作用:提高代码的复用性,精简代码;

   定义:修饰符 返回值类型 方法名(形式参数){}

   调用:静态方法(static修饰)方法名(实际参数);

        实例方法 实例对象.方法名(实际参数);

   方法重载:

     方法名相同,但形参的数量,类型,顺序不同;

     注意:方法重载与修饰符和返回值类型无关;

17、数组

   定义:是可以存储多个同种类型的数据结构

   特点:
       1、数组在内存空间上是一块连续的内存空间;

       2、其地址空间是连续的;

       3、查询效率极高,但增加删除的效率极低(末尾的增加和删除没有效率影响);

       4、数组存储的数据范围不超过int类型;

       5、数组不能存储大数据量,因为在内存空间上没有特别大且连续的空间

18、地址值

   
    在计算机中每一个字节都会有一个编号,这个编号就是内存地址,内存地址一般是十六进制;

  


19、面向对象


     1、类:

        一类具有相同性质的事物的总称;

       基本组成部分:属性,方法;

          属性(对类的静态描述)

          方法(类的行为)

        每一个类就是一个类型


    2、对象

       类的实例化/具体化;

    类与对象的关系:

       类是对对象的抽象,对象是类的实例/具体;

   3、类的定义:

       public class 类名{

          //成员变量(属性)
          修饰符 (任意)数据类型;

          //成员方法
          修饰符 返回值类型 方法名(){

          }
       }

    4、对象的创建语法:

      步骤:
        
        1、导包:java.lang包里的类不用导,同包下的类不用导,不同包下的类需要导入,

            格式:imort 报名.类名;  eclipse下导包的快捷键 window下 alt+? Mac下 option + ?(快速补全的快捷键也是这个)
            
        2、创建对象

            格式:类型对象 引用(标识符)(保存的是对象在内存中的地址值) = new 类名();

            咧: Person p = new Person();

        3、使用(调用类的成员变量 / 成员方法);
   
           格式:引用.成员方法() / 引用.成员变量;

            咧: p.age; p.eat();

    5、对象在内存中的位置:

          每 new 一个对象都会在堆内存空间1中开辟一块新的空间用来存储该对象的属性;


    6、使用自定义类型作为方法的参数

       方法的参数可以是任意类型
  
       对象(自定义类型)作为参数,传递的是地址值

    7、使用自定义类型作为方法的返回值

       方法的返回值可以是任意类型

       对象作为返回值,返回的是地址值。

    8、成员变量和局部变量的区别

       位置              作用范围            默认值

     成员变量        类里面,方法外面        整个类都可见      有,和数组中一样

     局部变量        方法里面                该方法中        没有,必须先赋值再使用

       成员变量在堆中,随着对象的创建而产生,随着垃圾回收而消亡。(生命周期长)相对  

       局部变量在栈中,随着方法的入栈而产生,随着方法的出栈而消亡。(生命周期短)相对

20、 this关键字

           this:区分同名的局部变量和成员变量 

           什么是this:谁调用方法,谁就是this


             1.局部变量和成员变量可以同名吗?可以

              2.使用的时候用的是哪一个:如果有同名的局部变量和成员变量,局部变量的优先级比较高

              3.加了this指向的是成员变量,不加this指向的是局部变量

21、构造方法

        使用:new对象时会调用构造方法

        构造方法的作用:

           无参数的:创建对象

           有参数的:创建对象并且给成员变量赋值
        
        语法:

             修饰符 方法名(参数列表){}
        强调:

            a.方法名和类名完全一致

            b.没有返回值,连void都没有
            
        其他注意事项:

           1.如果没有显示的写一个构造方法,那么默认会赠送一个无参数的构造方法

           2.如果显示的写了一个构造方法,则没有默认赠送

           3.构造方法可以重载

           4.一般我们定义一个类的时候会有一个无参数的构造方法,再来一个全参数的构造方法。(建议)

22、面向对象三大特性:封装,继承,多态;

       1、封装:

         private关键字的作用及使用

         private:

            操作成员变量的时候,有可能会有非法赋值

         过程:

          1、私有化成员变量:

              不能直接操作成员变量:私有化成员变量,用private来修饰成员变量
   
          2、提供相应公有的getter和setter方法:

              取值(getter):getXxx();要有返回值

              咧:public 返回值类型 getPrice(){

                return 返回值类型;
            }

            赋值(setter):setXxx(数据类型 参数名);没有返回值,但要有参数

              咧:public void setPrice(形参){
                     
                     this.成员变量 = 形参;

                     咧:this.price = price(形参);
                
              }

     2、继承:

           共性抽取,提高代码的复用性。

            多个类中,如果有相同功能的代码,可以进行提取(把相同的代码)写在一个父类。

            子类去继承父类,子类中可以直接使用父类中(非private修饰的成员信息) 成员信息。

             使用protected修饰的成员变量,子类可以直接使用。 

            语法:

                  修饰符 class 子类 extends 父类{
               
                   }

              继承关系下成员变量的访问特点:=左边是谁,访问的就是谁

               一个类如果没有显示的继承一个父类,则默认继承自Object顶级父类

               继承关系下成员方法的访问特点:new的是谁,访问的就是谁的方法。

              方法覆盖重写:在继承关系下

                     方法名相同,参数项相同,

                     修饰符相同

                      返回值相同

                @Override添加在方法上面去判断是否是一个正确的覆盖重写。

                        加上这个注解不报错,就是正确的覆盖重写。

                        加上这个注解报错,不是正确的覆盖重写

            

               继承关系下构造方法的访问特点:

                        在子类的构造方法中,如果没有显示的写一个super(参数),则默认会赠送一个super();

                       如果显示的写了,则不再赠送。

                       如果要显示的写一个super(参数),必须是第一个语句。
       
                       创建子类对象的时候,都会调用到父类的构造方法。

             super关键字的三种用法:

                     super.父类的成员变量

                      super.父类的成员方法

                     super(参数);//访问父类的构造方法。必须写在子类的构造方法中。
 
              this关键字的三种用法:

                     this.本类成员变量

                     this.本类成员方法

                     this(参数);//调用本类的其他(自己不能调用自己)构造方法

                     this(参数),必须是构造方法中的第一个语句。

                     和super()不能同时存在,如果显示的写了this()则不再赠送super()了。
  
                    注意事项:

                    构造方法不能递归

             继承的特点:

                    1. Java是单继承,一个子类只能有一个直接父类

                    2. Java是多级继承,父类还可以有父类

                    3. 一个父类可以有多个子类
  
              Object是顶级父类

    3、多态 

        条件:在继承关系下或者实现关系下;

        概念:一个事物(对象)的多种形态。

        特点:

            1、多态场景下,成员变量访问特点:编译看左边,运行看左边;

            2、多态场景下,成员方法访问特点:编译看左边,运行看右边;

        分类:

        1、典型写法(向上转型)

           父类型的引用 指向子类型的对象;

           接口类型的引用 指向实现类的对象;

          语法:
               父类型 引用 = new 子类名();

               接口类型 引用 = new 实现类名();

        2、向下转型

           子类型的引用 指向父类类型的对象;

           实现类的引用 指向接口类型的对象;

           语法:

               子类型 引用 = new 父类名();

               实现类型 引用 = new 接口名();

            向下转型有风险,可能会出现类型转换异常(ClassCastException) 需要在转型前进行判断;使用instanceof关键字

                 boolean b = 父类类型的对象 instanceof 子类;

                 boolean b = 接口类型的对象 instanceof 实现类;

        instanceof 类型判断:

            boolean b = 子类型对象 instanceof 父类型

     抽象类:
         1.使用abstract关键字修饰,位置修饰符的后面,class的前面
         2. 抽象类不能实例化(创建对象),但是抽象类中可以有构造方法(供子类调用)。
         3. 一个子类继承了一个父类则必须覆盖重写该父类中所有的抽象方法。
            除非子类是抽象的。
 
     抽象方法:
         1. 抽象方法没有方法体
         2.抽象方法使用abstract关键字来修饰。关键字的位置写在修饰符的后面,返回值的前面。
         3.抽象方法必须写在抽象类中。

23、如何定义一个标准的类

         四个组成部分:

                1.私有化属性

                2.getXxx setXxx

                3.无参数的构造方法

                4.全参数的构造方法

                5.彻底重写toString()和equals();

24、常用类

    1、ArrayList<E e> 数组集合类

       E get(int index);获取指定下标处的元素;

       E remove(int index) 通过指定下标移除某一个元素

       E remove(Object o)通过指定元素移除集合里某一个元素;
            如果集合里是元素 如果想通过元素去移除对应的元素时,但又因为该方法传递的是对象,

             所以此时应该将基本数据类型转为包装数据类型 如 Integer i = 100;
       boolean add(E e)将元素添加到列表末尾

       boolean add(int index ,Object o) 将元素添加到指定的索引处;

       clear() 清除元素列表内所有的元素;

       E  set(int index,E element) 将指定的位置处的元素修改为指定的元素

       int size() 返回集合的长度大小

     2、Math类

       random()

       nextInt(int a)返回一个0 ~ 1但不包含1范围内的数字 和Math.random()很像 

       如生成一个 0 ~ a (不包括)之间的数就是 : rd.nextInt(a)

       如果是包括的话就是:rd.nextInt(a+ 1);

       生成 x ~ y 之间都包含的是 :rd.nextInt(y - x +1) +x;

       Math:

       static double random();

       static final double PI = 3.14....;

       static int abs(int n);//绝对值

       static long round(double d) //四舍五入
       ;
       static double ceil(double d); //向上取整

       static double floor(double d);//向下取整


     3、一个类就是一种数据类型;


     4、泛型(未知的数据类型)E e;

     5、String类:

  
        特点:
            1. 字符串的内容可以共享

            2. 底层存储是使用char[] 数组

            3. 字符串的内容不能改变
  
        所属包:java.lang;

        构造方法:

             public String();

             public String(String str);

             public String(byte[] array);

             public String(byte[] array,int off,int len);

             public String(char[] array);

             public String(char[] array,int off,int len);

        成员方法:
        
             boolean equals(Object obj);

             boolean equalsIgnoreCase(String str);

             String toUpperCase();

             String toLowerCase();

             int length(); 

             char charAt(int index);

             String concat(String str);

             boolean contains(CharSequence cs);

             boolean endsWith(String str);

             boolean startsWith(String str);

             byte[] getBytes();

             char[] toCharArray();

             String substring(int beginIndex);//从beginIndex到字符串结尾

             String substring(int beginIndex,int endIndex);//从beginIndex到endIndex,左闭右开

             int indexOf(String str);

             int lastIndexOf(String str);

             String replace(char oldChar,char newChar);

             String replace(CharSequence oldStr,CharSequence newStr);

             String[] split(String regex)  

        String类里面做了一些特殊操作,打印对象的时候不是地址

        字符串的内容,是一个空字符串  "" 里面什么都不写就是空字符串


     6、static静态修饰符可以修饰:

       成员变量:

           位置:写在权限修饰符 和 数据类型 之间。

          被static修饰的成员变量,也叫做类变量,在内存中只有一份。

           访问方式:

               可以通过

               对象名取访问:不推荐

               可以通过类名去访问:推荐
  
       成员方法:

           访问:

              可以通过对象名取访问:不推荐

              可以通过类名去访问:推荐
  
           静态方法和成员方法:

               静态方法中:只能访问静态成员(变量、方法),不能访问非静态变量和非静态方法

               成员方法:可以访问静态成员,也可以访问非静态成员。
  
       静态代码块:

           位置:类里面,方法外面

           语法:

               static{
                   //静态代码块只执行一次。(一个类在jvm中加载起来以后就会执行静态代码块中的代码)
                   //先于构造方法执行
               }
  
               特点:只执行一次

               可以访问:只能访问静态成员,不能访问非静态成员

               作用:初始化静态变量(给静态变量赋值)


        构造代码块:

            语法:{}

            位置:类里面,方法外面;

            特点: 静态代码块在没次创建对象时都会调用,并且每一个构造方法里都会有构造代码块里的代码

            作用:可以将所有构造方法里的相同的代码放到构造代码块里 , 使得相同代码不用重复去写;

            原理:其实就是将这个代码到构造方法执行时,将构造代码块里的代码放到构造方法里,且是在最前面。  

       7、System类:

                static PrintStream out;

                static PrintStream err;

                static InputStream in

                static void arraycopy(Object src,int srcIndex,Object dest,int destIndex,int len);

                static Long currentTimeMillions

                String LineSeparator()输出系统的换行符

25、接口

       定义:一组功能的定义;

       语法:修饰符 interface 接口名{

            //接口中有静态常量  public static final可以省略 接口名去调用
            public static final 数据类型 常量名 = 对应数据类型的值

            //静态方法 public 可以省略  接口名去调用(推荐)
            public static 返回值类型 方法名(参数){}

            //抽象方法  public可以省略  实现类对象去调用
            public abstract 返回值类型 方法名(参数)

            //默认方法 public可以省略  实现类对象调用
            public default 返回值类型 方法名(参数){}

            //私有方法 或者静态私有方法 (jdk1.9后才有的新特性)
            private static 返回值类型 方法名(参数){}
       }

       类和接口的关系是实现关系   关键字 implements

       类可以实现多个接口,接口可以继承多个接口;

       语法:修饰符 class 类名 implements 接口名1,接口名2,接口名3{}

           实现类必须覆盖重写接口中所有的抽象方法,除非实现类是抽象类(可以重写一部分,也可以不重写)。

           实现类不一定要重写接口里的默认方法。

              如果重写了默认方法,则调用重写后的。

              如果没重写,则调用接口里的默认方法。

            实现类不能覆盖重写静态方法。通过接口名去访问静态方法

       特点:

          1、接口无法被实例法;

          2、接口里不能有构造方法,不能有静态代码块;

26、final关键字

   final关键字可以修饰:

      类:

         被修饰过的类不能被继承

         String,Math等类都是被static修饰的;

      成员变量(常量):

          特点:

              修改值:被final修饰的成员变量其值不能变:

                基本数据类型的值不能被改变

                引用数据类型的地址值不能不被改变,但对象的内容(实例变量的值)是可以被改变的

             赋值:必须赋值

                  赋值方式:

                    一、初识化的时候直接赋值

                    二、在构造方法里赋值,但这样,每一个构造方法里都需要单独赋值,因为创建爱你对象只会执行一次,所以这个单独赋的值只属于通过这个构造方法所创建的对象;

        成员方法:

               被final修饰的方法,不能被覆盖重写。

               final 和 abstract 不能同时使用。


        局部变量:

            被final修饰的局部变量其值不能变:

            基本数据类型:值不能变

            引用数据类型:地址值不能变


27、权限修饰符(Y:代表可以访问到                 N:代表不能访问到)

   

28、内部类

    1、成员内部类

       语法 :

        修饰符 class 外部类名{

            //成员属性

           // 成员方法

           // 成员内部类
  
           修饰符 class 内部类名{

           在成员内部类的成员方法中,可以直接访问外部类的成员信息

               //访问外部类同名的成员方法:外部类.this.成员方法
               //访问外部类同名的成员变量:外部类.this.成员变量

               //成员内部类字节码文件的名字:外部类$内部类
  
               //静态成员内部类中可以有静态成员也可以有非静态成员。

               //非静态成员内部类中只能有非静态成员,不能有静态成员

            }
        }

        创建成员内部类对象:

           外部类.成员内部类 对象名 = new 外部类().new 成员内部类();

           在外部类创建成员内部类的对象语法:

              内部类类型 引用 = new 内部类名();

    2、局部内部类

       在外部内的成员方法里的类 

       特点 :

         修饰符只能是abstract 或者 final 或者不写

         局部内部类里不能有静态成员

        使用范围:

           成员方法内

        访问同级的成员属性,只能取值不能赋值   

        使用:

           直接在方法内创建对象
           类型 引用 = new 类名(){};

    3、匿名内部内

       没有名字的类

       条件:

         在继承关系下或者实现关系下

       使用场景:如果一个类只使用一次(只创建一个对象),则使用匿名内部类比较简单

       语法:
           接口 对象名 = new 接口(){
               //覆盖重写方法
           };
  
           类名 对象名 = new 类名(){
  
           }.method();

           调用内部的方法或者变量

           new 类名(){
  
           }.method();

29、StringBuilder:

             作用:做字符串的多次拼接,使用StringBuilder效率高。

             所属包:java.lang;

             构造方法:

                 public StringBuilder();

             成员方法:

                 StringBuilder append(Object obj);//

                 String toString();

                 StringBuilder delete(int start,int end);//左闭右开

                 int length() 

                 StringBuilder reverse()字符串反转   

           底层:

           StringBuilder底层是char数组

           调用无参数的构造方法的时候:初始长度16

           扩容:新容量 = 旧容量 * 2 + 2;

        现在:String的+操作底层进行了优化,使用的也是StringBuilder ,但是String的 +  拼接操作 每一个 = 都会new一个 StringBuilder。

30、Date和Calender,SimpleDateFormate

    1、Date 

      构造方法:

            Date Date(); //获取当前系统的时间

            Date Date(long time)//指定的毫秒值所对应的系统时间的日期对象

      成员方法:

           long  getTime()//获取当前所在时间对应的毫秒值

    2、simpleDateFormate

       构造方法:
        
            SimpleDateFormate SimpleDateFormate(String patter);//创建日期格式化类  patter指定日期格式化的格式

       成员方法:

           String format(Date date);//将时间格式化成指定的时间

           Date parse(String str);//将字符串时间转换成时间对象

    3、Calendar

       所属包:java.util;

       无构造方法:

          但可以通过Calendar.getStance()得到Calendar对象;

       静态方法:
 
           static Calendar getInstance();//当前系统时间的日历对象
 
       静态常量:

           public static final int YEAR = 1;等等

        成员方法:

          Date  calendar.getTime() //获取当前系统的时间;

          int  calendar.get(int filed)//获取指定的年,月,日,星期等属性;

          void setTime(date);//指定calendar对象所指向的时间;

          void set(int year,int month,int day);

         void set(int field,int value); //指定calendar中某个属性的值;

         void add(int field,int count);//某个属性值增加

         void setTimeInMillis(long time);

         long getTimeInMillis();

31、Object类(顶级父类)

    任意引用数据类型未指定父类,默认是继承Object;

    构造方法:

       Object Object();

    成员方法:

       1、toString()

           返回的是包名.类名+@+"十六进制的哈希值(地址值)"

       2、equals(Onject obj)

           比较的是地址值

    注意事项:

         除了重写过toString方法和equals()的引用数据类型,其余都会默认调用Object的toString方法和equals();
 
     重写toString和equals():
     
         比较两个对象如果不想比较地址值,则可以在该类中覆盖重写equals方法。

         打印某个类型的对象,如果不想输出地址值,则需要覆盖重写toString方法。 

32、==和equals比较的是什么:

        ==:

            基本数据类型:比较值是否相等

            引用数据类型:地址值是否相等

        equals:

            Object类的equals方法比较的是地址值

            String类的equals方法比较的是字符串的内容(覆盖重写了Object的equals方法)

            自定义类的equals方法,根据覆盖重写的规则进行比较(一般比较的是两个对象成员变量的值)

33、常量与常量的拼接结果在常量池,原理是编译器优化。

    1、常量池中不会存在相同内容的常量。

    2、只要其中一个是变量,结果就在堆中。变量的拼接原理是StringBuilder。

    3、如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象的地址。

34、集合

     1、单列集合:Collection接口

        List接口:有索引值,有序,元素可重复。  数组

            ArrayList: 特点:查询快,增删慢,但对于末尾的元素的增删不存在效率的问题。底层是 Object[] 

            LinkedList:查询慢,增删快。 链表

            Vector:功能和ArrayList一致,线程安全,但效率低;

                   ArrayList线程不安全,但效率高;

        Set接口:无索引,元素不可重复

             HashSet:无序。哈希表

             LinkedHashSet:有序。链表+哈希表

             TreeSet:可排序。红黑树

        常用方法(Collection中的公共的方法,其下所有的子类都可以使用):

             Collection<E>接口中的常用方法:

             boolean add(E e); //向集合中添加元素

             boolean remove(Object obj); //从集合中移除某一个元素

             boolean contains(Object obj); //集合中是否包含某一个元素

             boolean isEmpty(); //集合里是否为空

             void clear(); //清空集合

             int size(); //集合的长度 元素个数

             Object[] toArray(); //把集合转为数组

             <T> T[] toArray(T[] array); //包含泛型的抽象方法

             Iterator<E> iterator()  //迭代器


   
   2、 双列集合:Map接口


         HashMap:无序。哈希表

         LinkedHashMap:有序。链表+哈希表

         TreeMap:可排序。红黑树

         Hashtable:功能和HashMap一致,线程安全,效率低。

                        HashMap线程不安全,效率高。

    3、Iterator<E>接口:

        无构造方法:但可以通过集合的iterator()生成Iterator对象

        成员方法:

            E next() //获取下一个元素;

            Boolean hasNext() //判断是否有下一个元素

            void  remove()  //移除当前元素;


    4、增强for循环(forEach)

       语法:for(被遍历的集合里的数据类型 变量名 : 被遍历的集合){
          
                         System.out.println(变量名) //变量名 每一项元素

                     }

        集合的遍历:

             1、hasNext() + next() + while()

             2、增强for循环  

        使用范围:集合,数组都可以使用      

35、泛型:未知的数据类型

       1、含有泛型的类:

          修饰符 class 类名<字母>{//定义类的时候不知道具体的类型

              //成员变量的类型可以是泛型类型

              注意:泛型不能实例化

              //方法的参数和返回值也可以是泛型类型
          }
          //创建对象的时候,尖括号里面写的是什么类型,泛型就是什么类型

          //如果尖括号中没有写具体的类型,则泛型是Object类型。

 
          修饰符 interface 接口名<字母>{

              //接口中的常量不能是泛型类型。

              //方法的参数和返回值可以是泛型类型

          }


 
       2、实现含有泛型的接口:

              1.实现该接口的时候,可以确定泛型的类型   

                   修饰符 class 类名 implements 接口名<具体的数据类型>{

                             //覆盖重写的方法中凡是泛型的地方,就可以写"具体的类型"

                     }
 
                    2.实现该接口的时候,不能确定泛型的类型。跟着写泛型。

                        修饰符 class 类名<泛型> implements 接口名<泛型>{

                                   //覆盖重写的方法中凡是泛型的地方,还是泛型
                           }

       3、含有泛型的方法:

                     修饰符 <T> 返回值类型 方法名(参数);


        4、泛型的通配符:

                        ?:可以表示任意的引用数据类型

        5、泛型的限定:

                       ? super T:泛型可以是T或者T的父类/接口类型

                       ? extends T:泛型可以是T或者T的子类/实现类


  36、Collection

    Collection 迭代器的实现原理:

       Collection<E>:

       Iterator<E> iterator();
  
  
       //下面这些实现类覆盖重写iterator方法以后,都要返回一个Iterator类型的对象。

       //下面这些实现类覆盖重写iterator方法以后,都要返回一个Iterator实现类的对象。


       
       ArrayList:要覆盖重写iterator()

       LinkedList  :要覆盖重写iterator()

       HashSet:要覆盖重写iterator()

       LinkedHashSet:要覆盖重写iterator()

       TreeSet:要覆盖重写iterator()

       迭代器的实现原理:一ArrayList为例去说明。()每一个实现类的迭代器实现原理都不同

            hasNext()

            next()
       
        public class ArrayList<E>{

            //成员变量
            private Object[] elementData; 

            private int size;
       
            //成员方法
            public Iterator<E> iterator(){

                return new Itr();
            }
       
            //成员内部类
            private class Itr implements Iterator<E> {

                //成员内部类的成员变量
                int cursor; 
       
                //成员内部类的成员方法
                public boolean hasNext(){

                    return cursor != size;
                }
                public E next(){

                    return (E) elementData[cursor++];
                }
            }
        }
       
        总结:

             1. hasNext方法,游标(初始值是0)和size进行比较,如果不相同就是有元素

             2. next方法,获取游标指向位置(索引值位置)的元素,然后游标+1


37、List接口


       List<E>接口:有索引值,有序(存入顺序和取出顺序一致),可以重复。


           常用方法

               E get(int index);

               void add(int index,E e);

               E set(int index,E e);

               E remove(int index);

               int indexOf(Object obj);

               int lastIndexOf(Object obj)   

        ArrayList:底层是数组。查询快、增删慢

        LinkedList:底层是双向链表。查询慢、 

        LinkedList类的独有方法(底层双向链表): 但是linkedList里可以通过index 获取元素,这个index是假的索引 

            E addFirst();//向头部添加元素

            E addLast();//向尾部添加元素

            E getFirst();//获取头部元素

            E getLast();//获取尾部元素

            E removeFirst();//移除头部元素

            E removeLast();//移除尾部元素

38、Set接口:

          特点:没有索引值,无序,不可重复

           HashSet:底层数据结构 哈希表(数组+链表+红黑树)。无序(新增顺序和取出顺序不一定 一致)

           LinkedHashSet:底层数据结构 链表+哈希表。有序

           TreeSet<E>:底层数据结构红黑树。可排序。
        
        成员方法:

           方法和collectiion里一致:没有额外扩展的功能。

          hashCode()

           因为Set集合是不允许元素重复的,因此默认会调用Object的hashCode方法,hashCode()的返回值的是地址值转换后的hash值;

           一般是不满足我们的需求,因此我们需要重写该方法。Idea生成;

        TreeSet


          构造方法:

               public TreeSet();

               public TreeSet(Comparator<E> c);


            重复:判断重复的标准

                哈希值相同 && equals相同

                哈希值:通过调用hashCode方法得来   


            1、因为TreeSet是可排序的,因此需要提供排序的规则,如果没有,则会报错;

            2、提供排序规则的方式

                1、要使用到的类里继承Comparable接口,并实现compareTo(),里面返回的是排序的规则 

                     可选择,升序或者降序;

                     升序:return this.XXX - o.getXXX();

                     降序:return o.getXXX - this.XXX;

              2、如果使用到的类没有实现compareTo(),则需要在new TreeSet()对象时里调用有参构造,里面传入CompareTor类型的对象,

                 这里就需要用到匿名内部类 比如:

                   Comparator<Person> comparator = new Comparator<Person>() {

                         @Override
                         public int compare(Person o1, Person o2) {

                              return  o2.getName().length() - o1.getName().length();
                         }
                   };

                  Set<Person> set = new TreeSet<>(comparator);

            使用TreeSet的时候,泛型必须是Comparable类型。如果不是则需要通过构造方法传入一个Comparator类型的对象。

 39、hashCode()和equals():

       1、对于需要大量并且快速的对比的话如果都用equals()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,
 
       2、如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equals()去再对比了),如果hashCode()相同,此时再对比他们的equals(),

         如果equals()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
  
       3、这种大量的并且快速的对象对比一般使用的hash容器中,比如HashSet,HashMap,HashTable等等,比如HashSet里要求对象不能重复,

          则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equals()验证,

          如果hashCode()都不同,则肯定不同,这样对比的效率就很高了。

       4、然而hashCode()和equals()一样都是基本类Object里的方法,而和equals()一样,Object里hashCode()里面只是返回当前对象的地址,

    如果是这样的话,那么我们相同的一个类,new两个对象,由于他们在内存里的地址不同,则他们的hashCode()不同,所以这显然不是我们想要的,

    所以我们必须重写我们类的hashCode()方法,即一个类,在hashCode()里面返回唯一的一个hash值。

40、两种比较器的区别与联系

     1、Comparable接口和Comparator的区别?

        1、位置不同:Comparable在java.


        2、实现方式不同:Comparable需要通过实现类去实现里面的ComparaTo()方法,Compatator是需要实现类实现里面的compare()方法

41、Collections类(集合工具类)

          addAll(Collection<? super T>c, T... elements)  T...表示可变参数 将一个数组或集合添加到一个list集合里;

     sort(List<T> list) 根据其元素的natural ordering对指定的列表进行排序。

     sort(List<T> list, Comparator<? super T>c)  对一个集合进行指定规则进行排序

42、Map接口 (双列集合)  

    1、Map<K,V>接口:

       1、特点:

           1. 一个元素有一个键(K)和一个值(V)两部分组成。

           2. K,V可以是任意的引用数据类型

           3. K不能重复(重复的标准和Set一样),一个K对应唯一的一个

       2、常用实现类:

               HashMap:底层是哈希表。无序

               LinkedHashMap:底层是链表+哈希表。有序

               TreeMap<K,V>:底层是红黑树。可排序(根据K来排序)。

               Hashtable:功能和HashMap一致,线程安全,效率低。

                           HashMap线程不安全,效率高。

                           HashTable的K和V不能为null,如果有为null,会报错NullPointer。  HashMap的K,V可以为null。

        3、构造方法:

            public TreeMap();

            public TreeMap(Comparator<K> c);

        4、常用方法:

              V put(K k,V v);//如果K重复,则返回被替换的V。如果K不重复,返回null

              V get(Object key);//根据K获取V,如果K不存在返回null

              V remove(Object key);//根据K删除元素,返回被删除元素的V,如果K不存在返回null

              boolean containsKey(Object key);

              boolean containsValue(Object value);

              boolean isEmpty();   

              void clear();

              int size();

              Collection<V> values();

              Set<K> keySet();

              Set<Entry<K,V>> entrySet();

        5、Set接口的底层就是Map

            new 一个 HashSet 底层会new一个 HashMap 

            new 一个 LinkedHashSet 底层会new一个 LinkedHashMap 

            new 一个 TreeSet 底层会new一个 TreeMap 
     
    2、Entry<K,V>接口:

             K getKey();

             V getValue();

43、数据结构

       栈:先进后出

       队列:先进先出

       数组:查询快,增删慢

       链表:查询慢,增删快

       红黑树:查询,增删 都比较快

44、HashSet的源码分析

   1、哈希表:(数组+链表+红黑树)  

       1、什么类型的数组:java.util.HashMap.Node<K,V>{
             int hash;

             K k;

             V v;

             Node<K,V> next;//单项链表

        2、数组的初识长度:16

        3、什么情况下会扩容:

          1. 当元素的个数占满"格子"的0.75的时候,会扩容。

          2. 当某个索引值位置下的元素>8个的时候 && 数组长度<64,会扩容   

        4、新容量:新容量=旧容量*2

            newCap = oldCap << 1

        5、红黑树:

            当某个索引值位置下的元素个数>8个 && 数组长度>=64,会把该索引值下的链表结构转成红黑树

        6、新增过程:

           1. 计算新增元素的 哈希值

           2. 哈希值%数组长度,确定新增元素的索引值位置。

               如果该位置没有元素:新增

               如果该位置有元素:则判断新增元素和该位置的元素是否重复。

                   如果相同:则不新增

                   如果不相同:挂在该索引值下链表的最末尾

        7、判断重复的标准:

            p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))

            哈希值相同 && (地址值相同 || equals相同)

    2、  Set:

           1、HashSet:

             构造方法:

                 public HashSet();

                 public HashSet(int capacity);//实际的长度是>=capacity的一个2的次方的值。

                 public HashSet(int capacity,float loadFactor)

            2、Set的底层就是Map。

    3、ArrayList:

        什么类型的数组:Object[]

        数组的长度:

            调用无参数的构造方法,会创建一个长度为0的数组,第一次调用新增方法,底层会创建一个长度是10的数组。

            有参数的构造方法,传入多大的数值,创建一个多大的数组

        扩容:newCapacity = oldCapacity + (oldCapacity >> 1)。

              新容量 = 旧容量的1.5

45、Throwable类(异常类)

     成员方法:

        void printStackTrace

    子类:

      1、Error:

         java.lang.StackOverflowError (栈溢出异常)

         java.lang.OutOfMemoryError    (OOM 内存)

    2、Exception:

          运行期异常:java命令运行.class文件。

              RuntimeException及其子类都是运行期异常。
 
          编译期异常:javac命令,把.java文件编译成.class文件时。

              ParseException

              Exception下,除了运行期异常都是编译期异常。
 
              必须处理,不处理不能通过编译。


    3、处理异常:try catch finally throws throw 

        1、try - catch代码块

          try{

              //有可能发生异常的代码

          }catch(XxxException e){

              //对异常的处理

              e.printStackTrace();

          }


        2、try - catch - finally

           try{

               //有可能发生异常的代码

           }catch(XxxException e){

               //对异常的处理

               e.printStackTrace();

           }finally{

               //无论是否发生异常,都要执行的代码

           }

        3、try - finally (很少这样写)
  

            try{

               //有可能发生异常的代码

            }finally{

               //无论是否发生异常,都要执行的代码

            }

        4、throws:声明某个方法抛出了异常

            位置:在方法小括号的后面大括号的前面

            不是彻底的处理异常的方式,只是让编译期异常能通过编译。

  
            遇到异常以后,不是直接结束程序。而是交给方法的调用处去处理。

            如果一路都没有处理异常,则最后交给jvm处理,jvm进行中断处理。

            如果自己没处理,二是抛给调用者,那么该异常代码后面的语句页将不会再执行;

        5、throw:抛出异常

            位置:方法里面 

            语法:throw 异常类型的对象

        6、遇到多个异常的处理方式:

            一个try catch块:多个异常之间有逻辑关系

            多个try catch块:多个异常之间没有逻辑关系

            一个try块,多个catch:多个异常之间有逻辑关系,但是发生异常以后,不同的异常希望有不同的处理逻辑。

                                如果多个catch块之间有子父类关系,则子类异常写面,父类异常写后面

        7、自定义异常

           需要继承某个异常一般为RuntimeException,再实现两个构造方式,并使用它。

46、进程与线程:

    1、进程:一个软件就是一个进程,一个软件可以有多个进程

    2、线程:一个软件可以有一个/多个线程;

    3、并行和并发:

       并行:多个任务在同一时间点(同时)去执行。多核CPU

       并发:多个任务在同一时间段(有先后顺序)去执行。

    4、调度方式:

       抢占式调度,先执行谁不确定,谁执行多长时间不确定。

    5、Thread类:

       多线程就是创建一个Thread类型的对象,启动执行。

       构造方法:

           public Thread();

           public Thread(String ThreadName);

           public Thread(Runnable target);

           public Thread(Runnable target, String ThreadName);

       静态方法:   

           static Thread currentThread();//获取当前执行代码的线程

           static void sleep(long millis);//单位是毫秒,程序睡眠millis毫秒

       成员方法:

           String getName();

           void setName(String name);

           void start();//线程启动以后会执行run方法中的代码

           void run();

           State getState();  //State 为枚举类 里面有定义了六种类型也就是线程的六种状态
 
           void join();

    6、多线程:就是创建一个Thread类型的对象,启动执行。

           好处:充分利用多核CPU的效率

    7、创建多线程的方式:

      1、继承Thread类:

           1、创建一个Thread的实现类,并重写run(),然后new 实现类对象 调用start();

             例: public class SubThread extends Thread{

                      @Override
                      public void run() {

                          System.out.println("hello java");
         
                      }
                 }

                 //创建一个线程
                 Thread t = new SubThread();

                 //启动线程
                 t.start();


           2、匿名内部类
        
              例:  //创建一个线程
                   Thread t = new Thread() {

                       @Override
                       public void run() {

                       System.out.println("通过匿名内部类,创建Thread类型的对象");
                 }
             };

             //启动线程
             t.start();

      2、实现Runnable接口,传入Runnable类型的target(使用有参构造方法)

         例: //通过匿名内部类的方式创建Runnable类型的对象

           Runnable r = new Runnable() {

               @Override
               public void run() {

                  System.out.println(Thread.currentThread().getName());

               }
            };

            Thread t = new Thread(r);

            t.setName("线程1");

            t.start();


        3、使用Runnable的方式比使用Thread类的方式的优势:

            1. 可以避免单继承的局限性。

            2. 任务和线程分离,实现了解耦合,提高了代码的健壮性。

            3. 在线程池中,只能传入Runnable,不能使用Thread。

   8、多线程的安全性:

        线程安全:

        概念:单线程没有问题,在多线程场景下,和预期结果不一致。

        什么情况下会产生线程安全:多个线程访问 同一个 资源 有可能发生线程安全问题

        解决线程安全问题的方式:

           1、 使用synchronized代码块

               synchronized(锁对象){//可以是任意类型的对象,但多个线程访问的必须是同一个锁对象。(可以使用类名.class 也是唯一的)

                   //有可能发生线程安全问题的代码
               }

               例子:  Thread t1 = new Thread() {

                          public void run() {

                              for (int i = 0; i < 100000; i++) {

                                  synchronized(this) {

                                      a++;

                                  }
                              }
                          }
                      };

            2、 synchronized方法

                方法的返回值和修饰符之间加上synchronized关键字

                静态方法:锁对象是当前类的反射对象

                非静态方法:锁对象是this

                例子:public static synchronized void increment() {

                            a++;
                     }

            3、 Lock锁

                Lock接口

                    void lock();//上锁

                    void unlock();//开锁

                实现类:

                    ReentrantLock类

                        构造方法:

                            public ReentrantLock();

                例子:Runnable r = new Runnable() {

                         @Override
                         public void run() {

                             for (int i = 0; i < 100000; i++) {

                                 //上锁
                                 lock.lock();

                                 a++;

                                 //释放锁   (必须手动释放,可以放到finally代码块里)
                                 lock.unlock();
                             }
                         }
                     };

    

    9、synchronized与Lock锁的对比?

       1、synchronized不管是同步代码块还是同步方法,都是在一对{}结束之后,才会释放锁对象(同步监视器);

       2、Lock是通过两个方法来控制需要同步的代码块,更加灵活;

       3、Lock作为接口,有更多的实现类,可以适用于更加复杂的场景;

   10、线程的状态:


 
       NEW:新建状态。一个线程创建好以后,启动之前。
 
       RUNNABLE:可运行状态。当线程装在执行任务。
 
       TERMINATED:消亡状态。线程把任务执行结束后。
 
       BLOCKED:阻塞状态,获取synchronized锁对象失败。
 
       WAITING:无限等待状态,获取Lock锁对象失败。
 
           t1.join了以后,t2就处于WAITING状态
 
       TIMED_WAITING:计时等待状态,执行sleep方法。
 
       新建状态 和 消亡状态 只能出现一次。

   11、栈,本地栈,程序计数器是线程独有的;

        方法区,堆,是线程共享的;

47、线程间的通信:

    1、 等待唤醒机制:

          A             B

         执行           等待

        执行结束唤醒B    执行

         等待         执行结束唤醒A


   2、Object类:


 
       void wait();//会让程序进入WAITING状态

       void wait(long millions);//会让线程进入TIMED_WAITING状态

           ☆ 这个和sleep的区别是:核心区别:Object的wait()会自动释放锁对象,而sleep()不会自动释放锁对象;  

           ☆ 释不释放的定义:就是某一线程执行完,等待的其他线程就能拿到锁对象;拿到就是指 -->: 能进入到synchronized代码块里


       void notify();//唤醒当前锁对象上等待的某一个线程

       void notifyAll();//唤醒当前锁对象上等待的所有线程
 
       注意事项:
 
           1. 上面方法在使用的时候必须在synchronized代码块 或者 方法中
 
           2. 调用上述方法的对象,必须和锁对象一致

    3、//获取当前类的反射对象。类名.class  (也就是获取当前类)

48、lambda表达式:


 
       作用:可以通过lambda的方式创建函数式接口类型的对象
 
       函数式接口:一个接口中只有一个抽象方法的接口。函数式接口可以使用@FunctionalInterface注解来修饰  Comparator 和Runnable都是函数式接口
 
       语法:
 
           ()->{}
 
           ():接口中抽象方法的参数。形参
 
              ->:固定写法
 
           {}:可以理解为对方法的覆盖重写

           例子一://通过lambda的方式创建Runnable类型的对象

               Runnable r = ()->{System.out.println("hello java");}; {} 里的就是重写方法里的方法体


             //通过匿名内部类的方式创建Comparator类型的对象

             Comparator<Integer> c = new Comparator<Integer>() {

                 @Override
                 public int compare(Integer o1, Integer o2) {

                    return o2-o1;

                 }
             };
        
            例子二: //通过lambda的方式创建Comparator类型的对象

             Comparator<Integer> c = (Integer o1, Integer o2)-> {

                 return o2-o1;
             };

49、死锁:

       当不同的线程占有对方需要的同步资源不放弃,都在等待对方放弃自己所需要的资源时,就会形成死锁

   当我们调用一个同步方法的时候,就已经相当于手里握了一把锁;

   一个线程可以同时握多个锁

   诱发死锁的原因:

       1、互斥条件

       2、占用且等待

       3、不可抢夺(不可抢占)

       4、循环等待

    解决死锁:

       1、死锁一旦出现,基本无法解决,尽量规避,可以考虑打破诱发条件;

         针对条件1:互斥条件下,基本很难人为干预;因为线程安全就是通过互斥条件达到的;

         针对条件2:可以考虑一次性申请所有的需要等待的资源,这样就不存在等待的问题;
         
         针对条件3:如果已经占有资源的线程,进一步申请资源,可以考虑在一定时间内拿不到资源,就可以释放手中的资源,供其他线程使用;

         针对条件4:可以考虑将资源改为线性顺序,先拿序号小的,这样就避免了循环等待;

50、线程池(ThreadPoolExecutor)

      存放了多个线程

     1、线程池的优点:

        (1)提高了响应速度;int prestartCoreThread()  int prestartAllCoreThread() 提前启动核心线程

        (2)降低资源消耗;(每个线程大约需要消耗1M的内存);

        (3)提高线程的可管理性;


    2、线程池构造方法的参数:

       int corePoolSize,   //核心线程数量。

       int maximumPoolSize,//最大线程数量

       long keepAliveTime,//线程存活时间

       TimeUnit unit,//时间单位

       BlockingQueue<Runnable> workQueue,//工作队列 (核心线程数量满了后,线程存放的队列,先进先出)

       ThreadFactory threadFactory,//线程工厂(生产线程)

       RejectedExecutionHandler handler//拒绝策略(当已达到最大线程数量后,执行的拒绝策略)

       成员方法:

            int getActiveCount();//获取正在执行任务的最大线程数量
   
            int getCorePoolSize();//获取核心线程数
   
            int prestartAllCoreThreads();//可以提前启动核心线程数量的线程。

            int getLargestPoolSize() //获取线程池中同时进行的最大线程数。

            int getPoolSize()  //返回池中当前的线程数。

        
 3、线程池执行任务的流程步骤:

            1. 有任务时,先启动核心线程数量的线程去执行。

            2. 当核心线程消耗完后,把任务存入工作队列中

            3. 当工作队列存满以后,会启动最大线程数量 线程去执行任务

            4. 当线程数量达到最大线程数量的时候,还有任务,则启动拒绝策略。

        存活时间:线程空闲以后,到了指定的时间,回收大于核心线程数量的线程

        拒绝策略:

            AbortPolicy:抛出异常

            CallerRunsPolicy:由主线程去执行任务

            DiscardPolicy:不执行任务,但是不抛出异常(就是将加入的任务丢弃掉)

            DiscardOldestPolicy 如果已经达到最大线程池数量,会将工作队列中最早的线程丢弃掉,存入新执行的任务。


    4、Executors类:

       静态方法:

          static ExecutorService newFixedThreadPool(int nThread);//固定线程池
 
          static ExecutorService newCachedThreadPool();//缓存线程池
 
          static ExecutorService newSingleThreadExecutor();//单例线程池
 
          static ScheduledExecutorService newScheduledThreadPool(int nThread);//调度线程池

    5、ExecutorService接口:

        成员方法:

          void executor(Runnable r);//开启线程执行任务 (一次一个) (有返回值)

          <T> Future<T> submit(Callable<T> c);  //开启线程执行任务(有返回值)

          Future<?> submit(Runnable r);  //开启线程执行任务  (没有返回值)

          void shutdown(); //关闭线程池

    6、Future接口

       成员方法

          <T> get(); //用于获取线程执行任务结束后的返回值  (也就是调用submit(Callable callable)才有值)不然结果就为空,也就是各类型的默认值。

    7、ScheduledExecutorService接口:

        成员方法:

          ScheduledFuture<?> schedule(Runnable r,long delay,TimeUnit tu);//开启线程  (没有返回值)
 
          <T> ScheduledFuture<T> schedule(Callable<T> r,long delay,TimeUnit tu);//里面执行

Runnable类型的对象。有返回值
 
          ScheduledFuture<?> scheduleAtFixedRate(Runnable r,long initialDelay,long

period,TimeUnit tu); //没有返回值 initialDelay 需要等待多长时间才执行第一次  period  后面每隔多

长时间开启一个线程  tu 时间单位。

51、File类:


 
       一个File类型的对象表示一个文件或目录
 
       一个文件 或 目录 是否存在,不影响File对象的创建。
 
       所属包:java.io;
 
       构造方法:
 
           public File(String path);
 
           public File(String parent,String child);
 
           public File(File parent,String child);

 
       成员方法:
 
           String getAbsolutePath();//获取绝对路径。带盘符的路径
 
           String getPath();//获取相对路径。相对于当前项目的根目录
 
           String getParent();//获取父路径
 
           String getName();//获取文件或目录的名称
 
           long length();//单位是字节。获取文件的大小,如果文件不存在,返回0.
 
           boolean isFile();//是否是文件,是true,不是false,不存在是false

           boolean isDirectory();//是否是目录,是true,不是false,不存在是false
 
           boolean createNewFile();//创建文件(不能创建文件夹),成功true,失败是false(存在继续创建)
 
           boolean delete();//删除文件 或 目录(空目录)
 
           boolean mkdir();//创建目录
 
           boolean mkdirs();//创建多层目录
 
           boolean exists();
 
           String[] list();

           String[] list(FileFilter filter)
 
           File[] listFiles();

           File[] listFiles(FileFilter filter); //FileFilter是一个函数式接口,可使用lambda表达式

        递归:

           直接递归:方法自己调用自己

           间接递归:a方法调用b方法,b方法调用a方法。
      
           注意事项:

               1. 递归必须要退出机制

               2. 即使有退出机制,递归的次数也不能太多

   ⭐️: 斐波拉契数列: 第一,二项为1,后面每一项都是前两项之后  1 1 2 3 5 8 13 21 34  fn(i-1)+fn(i-2)  结束条件 i==1 或 i==2

52、深浅拷贝:

       要克隆一个对象,调用Object父类的clone方法
 
       该方法是被protected修饰的,不能直接使用,使用的类要覆盖重写该方法。
 
       要调用clone方法的类必须是Cloneable(接口)类型。需要实现了接口
 
       深拷贝:两个成员变量(引用数据类型)的地址值不相同
 
       浅拷贝:两个成员变量(引用数据类型)的地址值相同

53、IO(输入输出)

   1、InputStrem和OutputStrem(字节输入,输出流)

        IO可以操作文件(读,写);

       1、基准:

           数据出内存:写

           数据进内存:读


       2、字节输出流(OutputStream抽象类)

           常用方法:

               void write(int data);//写出一个字节的数据

               void write(byte[] array);

               void write(byte[] array,int offset,int len);

               void close();

           常用子类:FileOutputStream
 
                  构造方法:
 
                      //如果文件不存在,会自动创建。但是如果某一层目录不存在,则会抛出FileNotFoundException
 
                      public FileOutputStream(String path);
 
                      public FileOutputStream(File file);
 
                      public FileOutputStream(String path,boolean append);
 
                      //上面三个构造方法,都是调用的这一个构造方法
 
            ⭐️        public FileOutputStream(File file,boolean append);
 
                      //append:false表示覆盖,true表示追加。

       3、字节输入流(InputStream抽象类)
 
              常用方法:
 
                  int read();//读取一个字节的数据,返回读取的数据,如果没有数据则返回-1
 
                  int read(byte[] array);//读取数据存入参数数组中,返回读取的长度,如果没有数据则返回-1
 
                  void close();
 
              常用子类:FileInputStream
 
                  构造方法:
 
                      public FileInputStream(String path);
 
                      public FileInputStream(File path);

       

     2、字符输出流:Writer


            常用方法:
      
                void write(int data);//写出一个字符的数据
      
                void write(char[] array);
      
                void write(char[] array,int off,int len);
      
                void write(String str);
      
                void write(String str,int off,int len);
      
                void close();
      

      
            常用子类:FileWriter
      
        ⭐️:     调用下面构造方法的时候,底层会创建一个长度是8192的byte数组(缓冲区)。
      
                调用写出方法的时候,会把字符转换成字节,存入缓冲区中。
      
                当byte数组存不下数据 或者 显示的调用flush方法,会把缓冲区中的数据写出去。
      
                调用close方法的时候,底层会调用到flush方法。
      
      
                构造方法:
      
                    public FileWriter(String path);
      
                    public FileWriter(File path);
      
                    public FileWriter(String path,boolean append);
      
                    public FileWriter(File path,boolean append);

      3、处理IO异常注意的点(资源需关闭):

         方式1:   try with resource:会自动释放资源(比如:流不需要显示的调用close(),会帮你自动调用close() )
     
         try(资源对象的创建){
     
             //有可能发生异常的代码
     
         }catch(XxxException){
     
             //对异常的处理
     
         }
     
         资源对象: java.lang.AutoCloseable类型的对象就是资源对象

         例子: try (
               
            ⭐️          Reader r = new FileReader("/Users/a13571442004/Desktop/a.txt");
               
            ⭐️          Writer w = new FileWriter("a.txt");

                ) {
           
                     char[] ch = new char[1024 * 10];
            
                     long start = System.currentTimeMillis();
           
                     while (true) {
           
                         int len = r.read(ch);
           
                         if (len == -1) {
           
                             break;
           
                         }
            
                         w.write(ch);
            
             ⭐️          w.flush();
        
                     }
           
                     long end = System.currentTimeMillis();
           
                     System.out.println("复制完成" + (end - start));
        
                 } catch (IOException e) {
        
                     e.printStackTrace();
                 }
       方式2:   使用try - catch - finally:

           例子:
                  Reader r =null;
                  
                  Writer w =null;
       
                  try {
        
                      r = new FileReader("/Users/a13571442004/Desktop/TCP套接字.docx");
        
                      w = new FileWriter("副本1.docx");
        
                      char[] ch = new char[1024*10];
        
                      long start = System.currentTimeMillis();
        
                      while (true) {
        
                          int len = r.read(ch);
        
                          if (len == -1) {
        
                              break;
        
                          }
        
                          w.write(ch);
        
                          w.flush();
        
                      }
        
                      long end = System.currentTimeMillis();
        
                      System.out.println("复制完成"+(end-start));
        
                  } catch (IOException e) {
        
                      e.printStackTrace();
        
                  } finally {
        
                      try {
        
                ⭐️        r.close();
        
                      } catch (IOException e) {
        
                          e.printStackTrace();
        
                      }
        
                      try {
        
                ⭐️          w.close();
        
                      } catch (IOException e) {
        
                          e.printStackTrace();
         
                      }

            方式3:throw

4、缓冲流

       1、字节缓冲流(会在new 对象时,在底层创建一个byte[8192]的缓冲区 默认)

          1、字节输出缓冲流(BufferedOutputStream)

             优点:减少写出次数,提高写出效率。

             构造方法:

                   调用下面构造方法的时候,底层会创建一个长度是8192的byte数组
 
                   写出的数据存入8192的byte数组中,
 
                   当该数组存不下数据 或者 显示的调用flush方法,则会把8192byte数组中的数据写出到目标位置。

                       BufferedOutputStream(OutputStream os);

                       BufferedOutputStream(OutputStream os,int size);size 设置缓冲区的大小

             成员方法:

                 void write(byte b)//只写一个字节的数据

                 void write(byte[] ba)//可以写一个数组的字节

           2、字节输入缓冲流(BufferedInputStream)

             优点:减少读取次数,提高读取效率
               
             构造方法:

                   调用下面构造方法底层会创建一个长度是8192/size的byte数组

                   调用读取方法,会一次读取8192字节的数据,存入该byte数组中

                   从对应索引值位置获取数据,进行操作。    

                         BufferedIntputStream(IntputStream os);

                         BufferedIntputStream(IntputStream os,int size);size 设置缓冲区的大小

             成员方法:

                 void read()//只读一个字节的数据

                 void read(byte[] ba)//可以读取一个数组的字节

                 void read(byet[] ba,int src,int len)//读取指定长度的字节


        2、字符缓冲流(会在new 对象时差创建一个char[8192]的缓冲区  默认长度 8192 )
            
            1、字符输出缓冲流(BufferedWriter)

                 优点:高效:字符-->字节

                 构造方法:

                    调用下面构造方法,底层会创建一个长度是8192/size的char数组
  
                    写出的数据存入8192的char数组中,然后当存不下 或者 调用flush方法
  
                    会把8192char数组中的数据 转换 存入 8192的byte数组中
  
                    写出到目标位置。
                 
                        BufferedWriter(Writer w);

                        BufferedWriter(Writer w,int size);

                 成员方法:

                    void write()写一个字符

                    void newLine() 写出一个换行符 

            2、字符输入缓冲流(BufferedReader)


                优点:高效:字节-->字符

                构造方法:
                   
                   调用下面构造方法的时候,底层会创建一个长度是8192/size的char数组
                   
                   当调用读取方法的时候,会把读取的数据存入8192的byte数组,然后转换(字节-->字符)存入8192/size的char数组中
                   
                   操作的数据,从8192/size的char数组中获取。

                      BufferedReader(Reader r)

                      BufferedReader(Reader r,int size)

                成员方法:

                   void read() 读一个字符

                   void readLine() 读一行字符

  5、转换流

        1、输出转换流(OutputStreamWriter)

           构造方法:

             OutputStreamWriter(OutputStream os)字符集默认的为编辑器的编码

              OutputStreamWriter(OutputStream os,String charset)

            成员方法:

               OutputStream里的方法它都实现了


        2、输入转换流(InputStreamReader)

            构造方法:  

               InputStreamReader(InputStream is) 字符集默认的为编辑器的编码

               InputStreamReader(InputStream is,String charset)

            成员方法:
  
               IntputStream里的方法它都实现了

      6、对象流

         1、序列化输出流(ObjectOutputStream)

            构造方法:

                ObjectOutputStream(OutputStream os)

            成员方法:

               void writeObject(Object obj)  将对象序列化  该对象必须实现了SeriaLizable接口


        2、反序列化输入流(ObjectInputStream)

            构造方法:

                ObjectInputStream(InputStream is)

            成员方法:

                Object readObject();

        3、序列化&反序列化注意事项:

            1、操作的对象必须是Serializable类型,因此,要操作的对象必须实现Serializable接口,

                其对象成员变量,也必须是Serializable类型;

            2、反序列化时的类结构要和序列化时的类结构要一致;如果不一致,会抛出异常;如果想要不一致,可以在该类添加一个常量serialVersionUID

               如: private static final long serialVersionUID = 1L;

            3、如果想让某个成员属性不参与序列化,可以使用transient关键字修饰。

      7、字符编码和字符集:

            字符集:
 
              ASCII字符集:
 
                 ASCII编码
 
              GBK字符集:
 
                 GBK字符编码:一个中文2字节
 
              Unicode字符集
 
                  UTF-8:一个中文3个字

53、Prpperties类

    继承了HashTable;

    Hashtable的方法它都能使用

    也有自己的独有的方法:

      Object setProperty(String Key,String vlaue);  //新增元素

      String getProperty() //通过键去获取值;

      Set<String> stringPropertyNames()//获取所有的键

      void store(OutputStream out,String comments) // 用于将Properties对象里的数据存储到某个文件里  comments  对于文件的说明,相当于注释 

      void store(Writer w, String comments)

      void load(InputStream in) //读取某个文件里的数据并读取到对象里

      void load(Reader r)

    ⭐️:一般有图片或者特殊格式的文件使用字节流,不然可能会出现乱码的情况

  54、网络编程:
 

软件结构:
 
           cs:客户端 和 服务器端
 
           bs:浏览器 和 服务器端
 
 
       网络编程有三要素:协议    ip  端口号
 
           协议:
 
               应用层
 
               传输层:
 
                   TCP:客户端和服务器区分,面向连接的协议。
 
                       连接:三次握手。断开连接:四次挥手。
 
                       特点:数据安全,效率低
 

 
                   UDP:面向无连接的协议。
 
                       特点:数据可能会丢失,但是效率高。
 
               网络层
 
               物理层
 

 
           ip:确定发送的目标位置
 
               ipconfig(window下)
 
               ipv4:a.b.c.d        //每个部分一个字节,四个部分就是四个字节
 
                                   //可以表示42亿多个地址
 
               ipv6:128位,8组,每组16位
 
           端口号:占用2个字节,范围0~65535
 

 
   表示本机的ip地址:
 
       127.0.0.1
 
       localhost
 
       本机的真实ip地址
 
   Socket类:
 
       所属包:java.net;
 
       构造方法:
 
           public Socket(String host,int port);
 
       成员方法:
 
           OutputStream getOutputStream()

           InputStream getInputStream()

           shutdownOutput() 告诉接收方已经传输完毕

           shutdownInput()
    
    UUID:

        静态方法:UUID randomUUID()

55、常用函数式接口:

   1、Suuplier<T t>

      抽象方法:

        <T> get();

    2、Predicate<T t>

       抽象方法:

            boolean test(T t);

        默认方法:

           Predicate<T> and(Predicate<? super T> other)

           Predicate<T> negate()

           Predicate<T> or(Predicate<? super T> other) 


           两个使用:比如

             Predicate <T t> p1;

             Predicate<V v> p2;

             p1.and(p2).test(T t>)


    3、Consumer<T t >

        抽象方法:

           void accept(T t);

        默认方法:
   
          Consumer<T> andThen(Consumer<? super T> after)

    4、Function<T t, V v> //T 要转换的数据类型  V 被转换的数据类型

        抽象方法:

            T t apply(V v)
  
        默认方法:


            <V> Function<T, V> andThen(Function<? super R, ? extends V> after)

56、枚举(ENUM)

     
      枚举:如果没有显示的写一个构造方法,则默认赠送一个无参数的构造方法。
 
      构造方法语法:
 
            修饰符 枚举(){}
 
            注意:枚举构造方法的修饰符只能是private
 

      

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值