java基础

Java的三大平台

JavaSE Java的标准平台:包含了Java的基础的功能,学习的语法,内置的API方法
JavaEE Java的企业级应用平台:JavaSE的基础上,扩展了一些API,用于B/S结构的企业应用的开发
JavaME Java的移动应用平台:JavaSE缩微版,给嵌入式系统或移动端系统使用过时

Java语言的特点:
(跨平台,简单,分布式,半编译半解释,健壮,安全,多线程)

   跨平台:同一版本的应用程序可以在不同的平台上执行;
   简单:不会出现类似于指针这种复杂的数据处理;
   分布式:Java支持在网络环境下对数据进行交互;
   半编译半解释:Java语言的执行过程是 首先将源文件编译成字节码文件,JVM虚拟机进行解释执行该字节码文件;
   健壮:Java中提供了自动垃圾回收机制和异常捕获机制,避免出现不必要的异常和内存溢出的情况;
   安全:Java中提供了专用的Java程序沙箱,重点在于保护用户不受从网上下载(破坏性)的程序所干扰
   多线程:支持Java实现更好的交互性和并发的处理;

   JRE:是运行环境
   JDK:是编译环境
   JVM:Java虚拟机

变量:用于存储程序在计算过程中用到的数据;

    变量的命名规范: 1.必须以字母、下划线、 美元符号开头;
                    2.余下的字母可以是下划线,美元符号或字母及数字,长度不限,不能含有空格;
                    3.不能使用Java中的关键字作为标识符;
    变量的命名:驼峰命名法,多单词命名 从第二个单词开始首字母大写;

    主程序: main方法,
        一个Java程序需要执行必须有一个main方法,main方法是程序的入口,需要程序执行的代码 一定要经过main方法;
   
    Java中的变量:   1.成员变量(属性)
                    2.类变量(static)
                    3.局部变量(方法中定义的变量)系统不会设置初始值;

问:
  什么时候使用变量?
    程序中的某些数据不固定时,可以声明变量表示;
    

数据类型:

Java中,数据类型分为两大类 (基本数据类型,引用数据类型);

基本数据类型:4类8种
  整形:   byte (占1个字节,一个字节占八位) -128~127
          short (占2个字节)              -2(15)~2(15)-1
          int (占4个字节)                -2(31)~2(31)-1
          long (占8个字节)               -2(63)~2(63)-1
                        
//在Java程序中直接书写的整数值 默认为int类型,如果想表示为long字符时,在数据的末尾加上L/l 不分大小写)
   
    浮点型:float (占4个字节) //单精度
            double(占8个字节)//双精度          
//声明小数float变量时,在后面加f,整数不需要
    字符型:char (只可放置一个字符,用单引号引起‘中’ ‘a’ ‘2’)
//默认值为空字符,char 是Java中的未赋值的原始数据类型,他是定长16的
布尔类型:boolean(默认值是false)
                  true 为真,表示成立
                  false 为假,表示不成立
                                
基本数据类型转换:
        隐式转换(自动转换):小的数据类型可以自动转换到大的数据类型;
        强制转换:大的数据类型转换到小的数据类型;
   
        例:(隐式转换 )
            布尔类型(boolean) 不能与其他的基本数据类型进行相互转换;
            byte short char 之间不进行相互转换;
            short --> int --> long --> float --> double
            
            byte short char 在参与运算时,会先转换为int;
        例:(强制转换)//进行强制转换,可能会造成数据精度的缺失
   
            int i=5;
            byte b=(byte)i; 在i前加(byte)就叫强制转换

关键字/标识符

java基础_第1张图片
image.png

问:
为何所有的东西都要包含在类中?
因为Java是面向对象的语言编程,一个程序只需要一个main()方法来运行;

main()方法的用途?
测试实体类,启动Java应用程序;

    ++:
        前置++,int n=2; ++n的初始值为1;
        
        后置++,n++的初始值为原始值,但是在之后使用n时,n的值是+1后的值;
        
    --;同理
    
    /(除法):若是两个整数相除,只保留整数部分;

    %(取余):被模数为正数结果都为正,被模数为负数结果都为负;
    
    算数运算符:
                如果参与除法运算的变量都是int类型,其结果也是int类型,
                如果参与除法运算的变量类型不一致,其结果以大的数据类型为准,
                求余结果的正负取决于第一个数的正负;
                
    辅助运算符:
                = 赋值,x=y 把变量y的值赋值给x;
                += , x+=y  x=x+y ;
                -= , x-=y  x=x-y ;

            n+=5  n=n+5 , m%=4 m=m%4;

运算符:

(用以表示数据的运算,赋值和比较运算的种类)

        算数运算符:+ - * / % ++ --
        赋值运算符:=  +=  -=  *=  /=  %=
        比较运算符:>  <  >=  <=  ==  != 
        逻辑运算符:! && ||
        位运算符:  %  |  ^  ~  >>  <<  >>>
        条件运算符:? :(条件运算符和独特,因为他是用三个操作数组成表达式的,三元运算符 特可以代替某种类型的if-else语句)
        
        (比较运算符就是比较两个数值或者变量之间的大小关系)
        (逻辑运算符比较两个或者多个变量之间的关系算式,结果为boolean类型)
        
    逻辑运算符:
                逻辑与&&:左右两边的表达式同时成立,整个表达式才成立,两端的操作值必须是布尔类型
   
                逻辑或||:左右两边的表达式,只要有一端成立,整个表达式才成立,如果两端的表达式都不成立,则整个表达式不成立;
                
                逻辑非!:对逻辑表达式的结果取相反的结果,对结果值进行取反需用!()括起要取反的值;
                
                            system.out.printle(a>b);//false;
                            system.out.printle(!(a>b));//true
                (短路问题:)
                            对于&&来说,如果左边的条件表达式结果明确为false,Java编译器不会在执行&&右边的条件表达式代码;
                            对于||来说,如果左边的条件表达式结果明确为true,Java编译器则不会在执行||右边的条件表达式代码;
                位运算符:(按位运算符可以对boolean操作)
                        & | 不会产生短路问题;(优先级 & > |)
                
                    按位与:只有参加运算的两位都为1,&运算的结果才为1,否则为0;
                    按位或:只有参加运算的两位都为0,|运算的结果才为0,否则为1;
   
    引用数据类型:当+号的左边或者右边出现字符串时,则加号不再是加法运算符,而是字符串拼接的功能;
   
    三元运算符:公式:条件表达式?表达式1:表达式2
                
                如果条件表达式的值为true,则返回表达式1的结果;
                如果条件表达式的值为false,则返回表达式2的结果;
                
                    system.out.printle(5==2?a+b:a-b);
            
    运算符的优先级:
                    算术运算符-->关系运算符-->逻辑运算符-->赋值运算符
                    (尽量多的使用括号,括号优先级别最高)

流程控制语句:

条件语句(if,swith语句 又叫分支结构)

        if(条件表达式){//代码块,语句块内容的代码值不固定;
                
            }
            如果条件表达式的结果为true,则执行语句块内的代码,
            如果条件表达式的结果为false,则不执行语句块内的代码;
   
        if-else:
            如果条件表达式的结果为true,则执行语句块内的代码,
            如果条件表达式的结果为false,则执行 else 语句块内的代码,前面的表达式结果不成立,则会确定最后值;
   
        if-else if:
            语句结构不固定,可以写多个else if条件,其中每个if语句块都是独立的,只会执行其中一个,
            如果存在多个条件成立的if语句块,则执行第一个成立的if语句块;
   
            (if语句表达式的返回结果必须是boolean类型)

switch语句:

                switch(num){//括号中的内容是整型数值 JDK1.7开始switch中可以使用字符串作为参数;
            case 1:
                    执行的代码
                break;
            case 2:
                    执行的代码
                break;
            default:
                    执行的代码
                }
                
            case常量必须是编译时常量,多个case使用相同的值是非法的;
            
            break:保证每个case 语句独立执行;
            
            default:在执行程序中没有想要执行的值,默认default()里的值,不是必须在case语句的结尾处出现;

循环语句:

什么时候使用循环语句?
  程序中需要反复执行的代码可以使用循环;

 ( while循环,do_while循环,for循环,死循环,嵌套循环,循环的中断)
        
        while(条件表达式){
            //条件表达式结果为true,则执行while语句块中的代码,代码执行之后再一次判断条件表达式的结果
            //是否为true,如果为true则继续执行代码,直到条件为false时,结束循环;
        }
        
        死循环:
            int m=10;
            while(m>1){
                system.out.printle(m);//添加m-- 即可
            }
        do-while;
            int n=1;
            do{
                sytem.outprintle(n);    
                n++;
            }while(n<=10);
            
        while 和do_while 的区别:
            while是先判断在执行,do_while是先执行在判断;
    
        for(循环变量的初始化,循环条件,循环变量的改变){
            //执行的代码
            //for 中定义的变量对for循环外部来说永远是不可见的
        }
    执行顺序:循环变量的初始化-->循环条件-->执行的代码-->循环变量的改变-->循环条件
   
        嵌套循环:
                for(int i=1;i<=5;i++){
                    for(int j=0;j<=5;j++){
                        system.out.ptintle(i+":"+j)
                    }
                }
                
        循环的中断:(break,continue)
   
            break:如果在循环结构中执行了break,整个循环停止,继续执行循环外的代码;
            
            continue:在循环结构中执行了continue会跳过当前循环,执行下一次循环;
            
        变量作用域问题:变量只在声明时它所在的快内有效;

控制台输入方法:

                    Scanner scan = new Scanner(System.in);
                        int num = scan.nextInt();
   
                    scan变量名可以改变,其他语法固定不变,
                    scan.nextInt()提供了在控制台输入数据的功能,nextInt()叫做方法,是Java提供使用的,
                    这个方法允许在控制台输入一个int类型的数据;
                    
                scan.nextInt();用于接收int类型数据
                scan.next();表示字符串

数组:

(数组实现了Cloneable接口,实现了Object类;)
    声明方式:   静态{只给长度}
                动态{赋值 也给长度}
        //数组用于存储一组相同数据类型的容器,通过元素的下标获取对应的内容;
        //数组中的数据叫做元素,每一个元素都有一个对应的下标,下标从零开始 依次递增,数组中元素的个数 表示数组的长度;
        //如果声明了具体的长度就不能声明具体的元素,如果声明了具体的元素 就不能声明具体长度;
        
        int[]中元素默认值是0,包括byte[],short[],long[];
        char[] 元素默认值是空字符;
        double[],float[]元素默认值0.0;
        boolean[] 元素默认值是false;
        
        创建int类型的数组: 
            int[] arr = new int[]{2,3,4};
            int[] arr = new {1,2,3};

            使用匿名数组创建语法时,不能指定数组的大小:new int[] {1,2}
            
            int 型数组不能引用其他类型的数组,数组的长度属性:length
            
        数组下标越界异常:java.lang.ArrayIndexOutOfBoundsException: 
            在编码时,如果使用了数组中不存在的下标,并不会出现编译报错,但在程序执行时 会出现数组下标越界异常;
    
            数组长度的语法是:length
            字符串长度的语法是:length()
            List集合长度方法时:size()
    
    多维数组(二维):
            int[][] arr = new int[2][3];
            int[][] arr = new int[][]{{4,5},{9,2,3}};
    
            for(int i=0;i max){
                    max = arr[i];
                }
            }
            System.out.println("最大值"+max);
        //求出数组中最小值及其下标:
        
            int[] arr = new int[]{12,45,10,23,50};
            
            int min = 60;
            int index = 0;
            for(int i=0;i arr[i]){
                    min = arr[i];
                    index = i;
                }
            }
            System.out.println("最小值"+min);
            System.out.println("下标"+index);
        
        
        空指针异常:java.lang.NullPointException
        由于内层数组并不存在的值为null,该声明方式不会编译报错,但运行程序出现空指针异常

冒泡排序:

原理升序:每一轮相邻的两个数进行两两比较,如果前一个数比后一个数大则交换位置,一轮之后找出最大的值,之后其余的数据进行新一轮的两两比较找出最大值,直到没有相邻的两个数在需要比较;

                int[] arr = new int[]{9,6,4,1,8};
                
                    for(int i=1;iarr[j+1]){
                                int temp = arr[j];
                                arr[j] = arr[j+1];
                                arr[j+1] = temp;
                            }
                        }
                    }
                    for(int i=0;i=0;i--){
                    arrNew[index] = arr[i];
                    index++;
            }
            for(int i=0;i

快速排序方法,只能按照升序排序;Arrays.sort()关键字

            int[] arr = new int[]{2,5,3,9,6};
                Arrays.sort(arr);
                
            for(int i=0;i

数组的复制和排序:System.arraycopy( 参1,参2 ,参3 ,参4 ,参5)关键字

     //第一个参数:源数组(被复制的数组)
     //第二个参数:从源数组的第几个下标开始复制元素;
     //第三个参数:目标数组(接收复制元素的新数组)
     //第四个参数:从目标数组的第几个下标开始放置元素;
     //第五个参数:从源数组中复制一个元素;

         int[] arrOld = new int[]{5,9,4,5,4,8};
         int[] arrNew = new int[6];
         
         System.arraycopy(arrOld, 1, arrNew, 0, 3);
         
         for(int i=0;i

Arrays.copyof( , );

        该方法会创建一个新数组:第一个参数 源数组
                                第二个参数 新数组的长度
   
        int[] arrOld = new int[]{5,9,4,5,4,8};
        int[] arrNew = Arrays.copyOf(arrOld, 6);
        
        for(int i=0;i

14班 排序:

    衡量排序三个指标:
        (a) 时间复杂度:时间复杂度越低,排序速度越快,一般衡量排序方法时,时间复杂度都是取决于最差情况下的时间。
            冒泡:O(n2)
            选择:O(n2)
            插入:O(n2)
            快速:O(n* logn)
        (b) 空间复杂度:空间复杂度越低,排序过程使用的内存空间越小
            冒泡:O(n)
            选择:O(n)
            插入:O(n)
            快速:O(n)
        (c) 是否稳定:内部相同数据的相对位置不发生改变,就是稳定排序(相同两个元素可能稳定排序,也能可不稳定排序)
            冒泡:稳定
            选择:不稳定
            插入:稳定
            快速:不稳定

   
        1.  冒泡排序法...
        
        2.  选择排序法:
                        升序原理:首选找到数组中最小的数,放在数组的第一个元素位置上,形成有序区,再找无序区中最小的数,
                                    放入无序区的第一个元素位置上,将无序区的第一个位置变成新的有序区;
   
                                int[] a = new int[]{22,14,35,15,27,6,21,8,16,11};
        
        
                                        for(int i=0;i 0 && a[j] < a[j-1]);
        }
        4.  快速排序法(了解)
                        原理:
                        升序:在数组中找到一个关键数据key,一般默认第一个元素是key,通过一系列操作将key的位置确定,key左侧都是比key小的数据,右侧都是比key大的数据。数组被key分为左右两部分,继续在两部分进行新key的寻找,直至数组不能继续划分位置。

jvm虚拟机:

            new 表示在堆中开辟一块空间,只要在堆中开辟空间,这块空间就会产生一个对应的地址,地址可以作为堆和栈连接的标识;
            数组空间的地址,又称为首地址 首地址也是数组第一个元素的地址;
                    
            栈属于线性结构:数组、链表也属于线性结构
            栈的特点:先进后出,后进先出
            
            堆和栈都属于线性结构;

方法定义及调用:(方法用于解决一段具有特定功能的代码)

定义:[修饰符] 返回值类型 方法名([参数类型 参数名]){方法体
      []内的内容可有可无
}
返回值:如果方法中的代码经过计算后会得到一个结果,那么该结果的数据类型,就是方法的返回值类型,
       如果方法中的代码没有结果值(输出语句不算结果值),则方法的返回值类型是void关键字;
//返回值类型包括:8个基本数据类型,字符串,数组
方法名:就是被执行被调用的标识;
参数:表示方法中需要的某些不确定的数据 数据类型 变量名,参数的个数不固定 根据方法的具体需求来定;
方法体:该方法核心功能的代码;

    具体:
        方法和方法之间是同级关系,不能在方法中定义方法,
        如果方法想要被执行,需要在main方法中调用该方法,
        程序中方法之间可以相互调用,但不建议调用main方法;
   
            public static void say(){
                return;//使用关键字return 返回该结果;
            }
            
    方法定义时:定义的参数叫做形参,调用该方法时 传入的值或者变量叫做实参;
            
    方法的调用:实际就是将实参传递给形参的过程,将实参传递给形参的一个过程 叫做传参;
   
    (一个类的构造方法是私有的时候,这个类的main方法是可以访问的,在main方法中可以直接new一个实例)

方法的重载:

//同一个类中多个方法具有相同的方法名,不同的参数列表称为方法的重载
      规则:方法名相同,
            返回值类型可以相同也可以不同,(方法的重载与返回值类型无关)
            参数列表不同 参数个数或者类型不同;

如何区分重载方法的调用:
根据参数列表的不同,调用不同的重载方法;

面向对象基础:(万物皆对象)

 面向对象编程思想:通常把构成问题域的事务分解成各个对象,给对象赋予相应的属性和行为,通过多个对象之间相互协调来解决问题,实际上就是一种运用对象、类、继承、封装、聚合、关联、消息、多态等概念来构造系统的软件开发方法;

 (面向过程解决问题思路:通常分析出解决问题所需要的步骤,然后用方法把这些步骤一步一步实现,最后一个一个依次调用方法来解决问题)

                 ATM提款机:客户 银行卡 现金 票据
             这些对象没有先后顺序,共同作用才构成了整个系统,我们只要用代码设计出这几个类型的对象,让他们之间互相通信 传递消息就可以完成系统功能;

     面向对象要点:
                 面向对象设计护展功能不需改动之前已经测试好的程序代码,
                 所有的Java程序都定义在类中,
                 对象称之为实例变量,他代表对象的状态,
                 可执行的称之为方法,他代表对象的行为;

什么是Java程序:

    //Java程序是由一组类所组成,其中有一个类会带有启动的main方法,多个类并以此进行提交;
       
        类是Java中一种特殊的数据类型;//引用数据类型
        类是一组具有相同特征和行为的事务抽象;
        类可以衍生出不同的对象,每个对象都有着共同的特征和行为,不同的对象之间不会相互影响;
   
        创建类的对象:类名 对象名 = new 构造器();
            
            通过对象.属性和对象.方法进行调用,类中的属性具有默认值;
   
        创建对象时,构造器的形式必须是在类中已经存在,否则无法使用 会出现编译报错;

构造器:

构造器的作用:完成对象的创建,即完成对象的实例化,一般使用构造器来完成对成员变量的初始化;
   
构造器(构造方法):
        编写一个类 一定要含有一个构造器,如果编写的类中 没有任何形式的构造器,
        Java编译器则会提供一个默认的构造器,如果类中已经存在一种形式的构造器,则编译器不在提供默认构造器;
        
构造方法要满足的条件:方法名和类名相同,
                    没有返回值
                    可以有参数,也可以无参数;
构造器的重载:
        通过有参构造器,在创建对象时,可以直接对属性赋值,对属性进行初始化操作;
        使用有参构造器对属性赋值,如果参数与属性名相同时,提供this.关键字进行区分;
        有参构造器调用无参构造器:在有参构造器中使用关键字this();表示当前类的无参构造器;

Java中有三种类型的变量:

    //成员变量(属性)
    //成员方法(方法)
        作用域优先级:作用域越小,优先级越高;
    
        局部变量:定义在方法中的变量或者是方法的形参;
                    只在方法的调用过程中有效,方法调用结束后失效;
        实例变量:即类的属性,也是全局变量;
                    类体中声明的成员变量为全局变量,全局变量在类的整个生命周期中都有效;
        类变量:在类中声明为static属性的变量,也叫静态变量;
                    用static修饰的成员变量,他们在类被载入是创建,只要类存在static 就存在;
                    
            除了8中基本数据类型的变量,其他变量都是引用数据类型;(类class,接口interface,数组[])
    
        Java中所有的变量、数组、对象都是分配在内存中的,根据变量类型的不同分配的内存也有所不同;
    
        内存的类别:
                    栈stack:栈的存取速度比堆快,效率高,在栈内保存基本类型的局部变量和对象的引用值;     
                    堆heap:堆中可以保存哪些对空间要求较大的变量,如对象的属性和数字元素,在堆内存中开辟空间只能通过内存分配操作符号new,凡是出现关键字new的地方必定分配了一个堆内存;

面向对象高级特性:( 继承,封装,多态,抽象 )

        子类 extends 父类
继承: 在Java中定义一个类时,让该类通过关键字extends继承一个已有的类,这就是类的继承(泛化);
            被继承的类称为父类(超类,基类),新的类称为子类(派生类);
            子类继承父类之后可以使用父类中的所有属性和方法(不包括私有的),但是父类不能使用子类中的属性和方法;
    
继承的好处:有利于程序的扩展,增强了代码的复用;
        
继承的特性:单一性继承,每个类只能有一个直接父类,一个父类可以有多个子类;
    
子类实例化过程:
                子类实例化时,先实例化其父类,在实例化子类;
                子类实例化时会先实例化其父类,父类构造器调用完毕,才执行子类的构造器;
                子类继承父类,在子类的构造方法的第一句一定会调用父类的构造方法;
                如果父类没有明确的构造方法,父类使用的是默认构造方法;
                在子类构造方法中会默认调用父类的默认构造器,super() 可以不写
                子类在自己的构造器中使用super关键字调用父类的构造器super(参数1);
                如果子类调用了父类的无参构造器,而父类中没有无参构造器则会报错;
    
super();关键字作用;
                调用父类的构造器,只能出现在子类的构造器中 且必须是第一行,super()中的参数决定了调用父类哪个构造器,
                如果子类构造器中没有出现super(),那么编译器会默认加上super() 即调用父类的空构造器,如果父类中没有空构造器 编译器会提示错误;
        
this();关键字作用:调用本类的构造器,只能写在构造器的第一行,在同一个构造器中super()和this() 不能同时出现;
        
    //子类在实例化时必须调用父类的构造器,实际上有的子类构造器也可以先调用本类的其他构造器,然会在通过那个构造器调用父类的构造器)
    
        实例方法:类中的普通方法
        类方法:类中的静态方法

包的概念及应用:

1.包允许将类组合成较小的单元
2.有助于避免命名的冲突
3.包允许在更广的范围内保护数据和方法
4.包可以是类、接口和子包的集合;
                    
            java.lang 语言包、任何程序中该报都自动导入;
            java.awt  图形用户界面包
            java.awt.event  图形用户界面事件处理包
            java.swint 跨平台轻量级组件包
    
            Java.sql 数据库访问包
            Java.io 这个包对您的输入/输出操作有用的类组成
            Java.util 该包提供了创建所需要的类和接口 如:lists calendar date
            Java.net 该包提供了许多进行 TCP/IP 网络编程的类和接口;
    
        import 导入包中的类:  1.导入包中的所有类 import 包名 * ;
                                2.导入子包中的所有类 import 包名 . 子报名 .*;
                                3.导入包中的某个类 import 包名 . 子包名 . 类名 ;
    
        注意:在Java中位于包中的类,在文件系统中的存放位置 必须有包名层次相对应的目录结构;
            package 语句作为java 源文件的第一条语句,每一个源文件只能声明一个包 如果没有package语句 则默认无包名;

封装:

//对类中的属性和方法规定访问权限,保护类中数据的安全,
//将类中的信息(属性和方法)隐藏起来,不允许外部程序之间访问,而是通过该类提供的方法进行访问;
    
访问权限:(用来控制类的成员和类的使用范围)
        
        private 私有的         同一类,(只能在当前类中进行访问)
        default 默认的     同一类,同一包中的类 (在同一包中的其他类)
        protected 受保护的  同一类,同一包中的类,不同包中的子类 (可以在不同包中的子类中访问)
        public 公共的      同一类,统一报中的类,不同包中的子类,其他包中的类 (在整个工程范围内)
   
        //访问权限可以修饰属性和方法,其中public 和default 的可以修饰类;
        
        //属性私有化,方法公共化(定义公共的方法给外界访问私有属性);

多态:

//子类和父类之间,不同的子类之间对于同一行为,有不同的实现方式
   
多态的多种实现:编译时多态、方法的重载;    (也称为前绑定)
              运行时多态、类的继承        (也称为后绑定)
              方法的重写 
              父类的引用指向子类
   
方法的重写:对从父类中继承的方法进行重新的改造;
            
方法重写的规则:方法名相同,
              参数列表相同 数量、类型、顺序相同,
              返回值类型相同;
              子类方法的访问权限不小于父类方法的访问权限
   
重载和重写的区别:(@override重写注解,@orverload重载注解)
                重写:遵循“运行期”的绑定,根据对象类型进行调用;
                重载:遵循“编译器”的绑定,根据参数列表不同进行绑定;
   
多态的体现:引用数据类型的转换
                上述造型(向上造型)父类的引用指向子类的对象;
                    
            //优点:有利于程序的维护和扩展;
            
        多态存在的三个必要条件:要有继承或实现,
                                要有重写
                                父类引用指向子类
                                
            //满足以上3个条件,当调用父类中被重写的方法后,运行是创建的是哪个子类的对象,就调用该子类中重写的哪个方法;
   
向上造型(上溯造型):父类的引用调用父类中存在的属性和方法,如果使用上溯造型中父类的引用调用重写的方法,则执行的是子类重写后的方法;
        1.子类转换为父类 自动转换
        2.前提_具有继承或实现关系
        3.向上转换损失了子类新扩展的属性和方法,仅可以使用从父类继承的属性和方法;
                            
向下造型(下溯造型):
        1.将父类对象显示的转换成子类类型 强制转换
        2.曾经向上换过的对象,才能在向下转换,对象不允许经过上溯造型而直接下溯造型;
   
//instan ceof运算符:经过上溯和下溯造型之后,我们很难知道某个引用到底指向那种类型的对象了;
//可以通过instan ceof来判断该经过上溯转型后是哪一个子类的;
                            
//instan ceof运算符的一般格式:  Object instanceof class
                            Object instanceof interface
                            返回值都是boolean

static 关键字:(静态的)

//static可以修饰属性、方法和代码块 但是不能修饰局部变量(方法中声明的变量叫做局部变量);
//static关键字 不能修饰顶层类,不能修饰类 不能修饰局部变量;
    
静态变量-(static修饰的变量叫做静态变量)
                该变量也称为类变量,被该类的所有对象共享,在类被加载时创建,只要类存在 静态变量就存在;
                
            //访问方式:类名.属性名 或 对象.属性名
静态方法-(static修饰的方法叫做静态方法)
                不需要实例化对象,可以直接访问,也称为类方法;
                
            //访问方式:类名.方法名 或 对象.方法名
静态方法的注意事项:静态方法中只能直接访问静态属性;
                  静态方法中不能使用this super
                  静态方法不能被重写,不能修饰构造器;
   
静态块-(static修饰的代码块)
       当前类被加载时,静态代码块被执行 且只执行一次,通常用来对属性进行初始化 加载静态资源;

final关键字:

//修饰的基本数据类型不能修改值;(变量上不能使用递增/递减运算符)
//修饰的引用数据类型不能修改地址;
    final修饰的类不能被继承;
    final修饰的方法不能被重写;
    final修饰的类属性不能被重新赋值;如果声明属性时使用final修饰,属性不再有默认值(初始值);
   
常量:所有对象共享、不需要经常修改的数据-static final,final常量在使用前必须被初始化,否则会产生编译报错;
常量命名规范:所有字母大写,单词之间用下划线;

单例模式(Singleton):

//保证一个类仅有一个实例,并提供一个访问他的全局访问点
   
基本数据类型之间是值传递
引用数据类型之间是地址传递

单例模式实现:拥有一个私有的构造器;
             提供一个自身静态的成员变量
             提供一个公有的静态方法;
   
单例的两种写法:
        
            public class Singleton {
                private Singleton(){}
                   //在自己内部定义一个实例,只供内部调用
                   private static Singleton instance = new Singleton();
                   //这里提供了一个供外部访问本class的静态方法,可以直接访问  
                   public static Singleton getInstance() {
                     return instance;   
                   }
                }
                
                public class Singleton {
                  private static Singleton instance = null;
                  public static synchronized Singleton getInstance() {
                  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次    
                  //使用时生成实例,提高了效率!
                  if (instance==null)
                    instance=new Singleton();
                return instance;   }
                }

抽象类(abstract):

//可以用来声明方法和类,如果方法是abstract的,那么类也必须是abstract的;

抽象方法:只有方法的声明,没有方法实现的方法,不能使用final和static修饰抽象方法;
抽象类:抽象类是抽象方法和非抽象方法的集合;
        
            [访问权限修饰符] abstract 返回值类型 抽象方法名(参数列表);
   
            [访问权限修饰符] abstract class 类名{
                                    abstracd int method(int a,int b);
            }
            public abstract void test1();//抽象方法
   
            public void test2(){}//普通方法
抽象类可以定义属性,但用处不大,
抽象类不能通过new构造器创建对象,
继承了抽象类的子类必须重写里面抽象方法,
抽象类中可以存在普通方法,也存在抽象方法;
   
抽象类的规则: 抽象类不能被实例化,
              其包含的抽象方法必须在其子类中被实现,否则该子类只能声明abstract
              抽象方法不能为static;
   
在以下情况中 一个类必须声明为抽象方法:

            当一个类的一个或多个方法是抽象的;
            当类是一个抽象的子类,并且没有实现父类的所有属性和方法 即只实现部门;
            当一个类实现一个接口,并且不能为全部抽象方法都提供实现的;

接口(interface):接口是默认的访问修饰符

//接口中的变量 默认为常量,
//接口中的方法 默认为抽象方法,
//接口不能实例化;
                        
            [访问权限修饰符] interface 接口名{
            
                    接口的成员:常量(字段),抽象方法;
                public abstract void run();
            }
            
            public interface DemoImp{
                    int n=5;//常量
                public void test1();//抽象方法
            }
            
            public class DImp implements DemoImp{
            }
   
接口的概念:接口中只包含常量和抽象方法,而没有变量和方法的实现;
          接口对类来说是一套规范,是一套协议;
          接口不是一个类 不能实例化;
   
注意: 接口不是一个类,没有构造器 不能被实例化,
      接口使用interface关键字来定义,而不是class,
      接口默认: 常量 public static final
                            抽象方法 public abstract
   
类实现接口(implements)接口实现类;
                
        为了使用一个接口,你要编写实现接口的类,
        如果一个类要实现一个接口,那么这个类就必须实现接口中所有抽象方法,否则这个类只能声明为抽象类,
        多个无关的类可以实现一个接口,一个类可以实现多个无关的接口,
        一个类可以在继承一个父类的同时,实现一个或多个接口;
   
抽象类和接口的区别:
                一个类只能继承单个类,但是可以实现多个接口,
                接口强调特定功能的实现,而抽象类强调所属关系,
                抽象类中的所有方法并不一定要是抽象的而接口要求所有的方法都必须是抽象的;

另:抽象类可以理解为抽象方法和非抽象方法的混合体,而接口中的方法完全是抽象 是一套纯粹的规范,
    一般来说 有关系的类才能继承同一个抽象类,而无关系的类不可能有同一个抽象父类,但是无关的类可以实现同一个接口;

异常处理:

        在使用try catch块时,如果try中没有响应的异常产生,则catch中不能有这种异常,否则编译报错,但在excp ...throw...中可以出现;

                public static void test1(){
                        //try{ }catch(){ }捕获异常
                        /*
                         * try块中写的是 可能发生异常的代码
                         * 
                         * catch括号中表示捕获的异常类型
                         * catch块中的代码 ,在发生对应类型的异常时,才会执行
                         */
                        try {
                            FileInputStream fis=new FileInputStream("D:/a.txt");
                            System.out.println("***");
                            
                        } catch (FileNotFoundException e) {
                            System.out.println("发生异常了...");
                            e.printStackTrace();
                        }finally{
                            //无论是否发生异常 最后都会执行该块的代码
                            System.out.println("finally");
                        }
                //通常是三种方法一起使用;
finally语句:
    
        finally语句放在try …catch语句后
        fianlly语句中的代码块不管异常是否被捕获总是要执行        
        对应finally代码中的语句,即使try代码块和catch代码块中使用了return语句退出当前方法或般若break跳出某个循环,相关的finally代码块都有执行。
        当try或catch代码块中执行了System.exit(0)时,finally代码块中的内容不被执行
                
throws关键字:(在方法的定义上抛出异常)

        如果一个方法中的语句执行时可能生成某种异常,但是并不能确定如何处理,则可以在程序所在的函数声明后,使用throws关键字抛出异常
        位置:函数参数列表的后面,throws关键字后面,可以跟多个异常,中间用逗号分割
    
throw语句:(在方法体内部抛出异常)

        异常是通过关键字 throw 抛出,程序可以用throw语句引发明确的异常;          
        throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。不能单独使用,要么和try.. catch…一起使用,要么和throws一起使用。   
                
//检查性异常:若系统运行时可能产生该类异常,则必须写出相应的处理代码,否则无法通过编译;
//非检查性异常:若系统运行时可能产生该类异常,则不必在程序中声明对该类异常的处理,就可以编译执行;
   
            运行时异常:Runtime Exception
            数字转换异常:NumberFoRmatException
            数据库异常:java.sql.SQLException
            所有异常的父类:java.lang.Exception
   
异常类型分为(检查性异常,非检查性异常)
    
                检查性异常 
            ClassNotFoundException 无法找到想要创建对象的类文件
            IOException I/O 异常的根类(文件与流)
            FileNotFoundException 不能找到文件
            EOFException 文件结束
            IllegalAccessException 对类的访问被拒绝
            NoSuchMethodException 请求的方法不存在
            InterruptedException 线程中断
    
                非检查性异常 
            RuntimeException java.lang包中多数异常的基类
            ArithmeticException 算术错误,如除以 0
            IllegalArgumentException 方法收到非法参数
            ArrayIndexOutOfBoundsException 数组下标出界
            NullPointerException 试图访问 null 对象引用
   
空指针异常:
            使用对象调用方法时,如果对象的值是null的话,执行代码会产生空指针异常,
            使用包装类的对象参与表达式运算时,如果对象的值是null,也会产生空指针异常;

垃圾回收机制

Java中的垃圾回收机制是自动的,垃圾回收机制实际上是JVM内部一个优先级比较低的后台线程,垃圾回收机制仅作用于堆,与栈无关

产生垃圾的情况

  • 对象的引用被赋值为null
  • 一次性使用匿名对象
  • 超出生命周期的对象

GC垃圾回收器:

当代码已经无法在访问对象的时候,这个对象就成为了可回收垃圾;

final,finally,finalize 区别是什么?

final修饰符,

finally:异常捕获机制的最后一个流程,无论是否产生异常都会执行,通常用于关闭资源;

finalize:当GC回收某个对象时,调用该对象的finalize();
            
finalize()是Object类中的方法:
                当一个对象变成垃圾,被GC回收时,会调用该对象的finalize(); 

请说明Integer和int的区别?
   Int 是基本年数据类型,Integr是引用数据类型属于int的包装类,Integer中包含了int,Integer更实用于Web应用项目;

指针: 是编程语言中的一个对象,通过地址 它的值指向电脑存储器中另一个地方的值,由于通过地址能找到所需的变量单元,将地址形象化的称为指针;
指针一般指向一个函数或一个变量,在使用一个指针时,一个程序即可以直接使用这个指针所存储的内存地址,有可以使用这个地址里储存的函数的值;

    Java中避免使用指针,一般出现在比较接近机器的语言,汇编语言或C语言

工具类:

Object类:Java中所有类的父类

创建一个自定义类, 类的对象会调用出一些
类中不存在的方法,这些方法的都是继承于Object类

Object类中的方法

equals(Object obj) -boolean
比较两个对象是否为同一个对象(实际比较对象的地址是否相同)

Person p=new Person(12,"jack");
Person p1=new Person(12,"jack");
System.out.println(p.equals(p1)); - -false

== 和equals 的区别

== :比较基本数据类型的变量时,比较的是变量的值是否相同
比较引用数据类型时,比较的两个对象的地址是否相同

equals():Obejct类中的方法,默认比较的是两个对象的地址是否相同
一般Object的子类会重写该方法,从而比较的的是内容是否相同

在Person类中重写equals( )
重写equals() 从而比较两个对象之间的内容是否相同
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
测试类

Person p=new Person(12,"jack");
Person p1=new Person(12,"jack");
System.out.println(p.equals(p1)); - -true

hashCode() 返回对象的哈希值(int)

重写equals()的同时 重写hashcode();

如果两个对象的equals()比较为true,则两个对象的hashCode值相同

如果两个对象的equals()比较为false 那么两个对象的hashCode值可能相同,也可能不同

toString( ) 返回对象的字符串表现形式

直接使用输出语句打印一个对象 会得到该对象地址的字符串表现显示
子类重写Object中的方法,直接输出的对象 会得到对象的属性信息

@Override
    public String toString() {
        return "Person [age=" + age + ", name=" + name + "]";
    }

包装类

包装类继承了8种数据转换
数据类型 char int
封装类 Characcter Integer 其余全部大写

  • 网页的默认信息为字符串类,用包装类将字符串转换为int类型(Integer)
//包装类和基本数据类型之间可以直接转换,包装类对于基本数据类型可以直接赋值
        Integer m=20;
        Integer k=n;

        System.out.println(Integer.MAX_VALUE);//int最大值 (2147483647)
        System.out.println(Integer.MIN_VALUE);//int最小值(-2147483648)
        
        //Integer.parseInt(String s) -int  返回值
        //将字符串转为int 方法中的字符串必须是纯数字形式的
        //否则会出现转换异常 java.lang.NumberFormatException
        String number="12";
        System.out.println(number+1);//121
        int a=Integer.parseInt(number);
        System.out.println(a+1);//13

        //字符串转换为包装类
        //Integer.valueOf(String s) - Integer
        String num1="12";
        Integer numi=Integer.valueOf(num1);
        System.out.println(numi);//12
//注意 字符串不能通过以上两种方式转换为Character

        //包装类转换为字符串
        //Integer.toString(int i) -String
        String str=Integer.toString(5);
        System.out.println(str+1);//1011

另:

//字符串通过构造方法转换为包装类
  String slnt = "500";
  Integer wlnt = new Integer(slnt);
  System.out.println(wlnt);

//包装类转换为基本数据类型 xxxValue();
  Integer wlnt = new Integer();
    int plnt = wlnt.intValue();
  System.out.println(plnt);

//字符串转换为基本数据类型 parsexxx(String s)
  String slnt = "500";
  int plnt = Integer.parseInt(slnt);
  System.out.println(slnt);

自动的装箱和自动拆箱

在进行基本数据类型和对应的包装类转换时 系统将自动进行;
JDK 5.0版本后引用
方便程序的编写

int plnt = 500;
Integer wlnt = plnt;
int n = wlnt;

除了Character之外,所有的包装类都提供2个构造函数;
一是基本数据类型
二是String表示的单元

Math类

Math类中的方法都是static 方法,包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数;

//Math类中的属性和方法 可以直接使用无需调用(数学)
        //圆周率的周长与直径之比的-double值
        System.out.println(Math.PI);
        
        //绝对值方法  Math.abs(int i) -int  
        System.out.println(Math.abs(-20));
        
        //求立方根方法  Math.cbrt(double d) - double
        System.out.println(Math.cbrt(8));
        
        //向上取整 ,返回大于或等于参数的最小整数  Math.ceil(double d) -double 
        System.out.println(Math.ceil(-0.0));
        
        //向下取整 ,返回小于或等于参数的最大整数 Math.floor(double d) -double
        System.out.println(Math.floor(0.9));
        
        //返回两个数的最大值  Math.max(int a, int b) -int 
        System.out.println(Math.max(5, 8));
        
        //返回两个数的最小值  Math.min(int a, int b) -int 
        System.out.println(Math.min(10, 8));
        
        //返回[0.0,1.0)之间的随机小数  Math.random() - double
        System.out.println(Math.random());
        
        //四舍五入  Math.round(double d) - long 
        System.out.println(Math.round(5.0));
        
        //求平方根方法   Math.sqrt(double d) -double
        System.out.println(Math.sqrt(25));
        
        System.out.println(Math.random()*10+5);
        
        Random ran=new Random();
        int d=ran.nextInt(1000);//[0,1000)
        System.out.println(d);

Date类

  • 创建Date对象
    Date类源码中重写 equals() 方法;

  • getTime( ) - long 获取当前时间距1970年的毫秒值

  • setTime( ) - Date 根据毫秒值来设置时间

Date date =new Date( ); 格林威治时间的当前系统时间

SimpleDateFormat 日期格式转换

        SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日  hh:mm:ss:SS");
        Date date=new Date(); 
        System.out.println(date);//Mon Aug 20 16:42:32 CST 2018
        
        --format(Date d) -String 将日期类型的数据转换成字符串形式
        String time1= sdf.format(date);
        System.out.println(time1);

SimpleDateFormat类中的模式字母

y      年
M      年份的月份
d      月份中的天数
E      星期中的天数
a      Am/pm标记
H      一天中的小时数(1-12)
h      am/pm中的小时数
m      小时中的分钟数
s      分钟中的秒数
S      毫秒数

Calendar类(日历类)/日期类

常用的日期处理的对象,可以设置自己的时区和国际化格式,是一个抽象类;

//获取Calendar对象的实例
Calendar cal=Calendar.getInstance();
//设置Calender实例所表示的时间
cal.set(2011, 6, 16);
 //获取指定的时间属性
cal.get(Calendar.YEAR);
        Calendar cal=Calendar.getInstance();
        
        System.out.println(Calendar.YEAR);
        System.out.println(cal.get(Calendar.YEAR));
        
        System.out.println(cal.get(Calendar.MONTH)+1);//月份比实际值少1
        
        System.out.println(cal.get(Calendar.DATE));
        
        System.out.println(cal.get(Calendar.HOUR));
        
        System.out.println(cal.get(Calendar.MINUTE));
        
        System.out.println(cal.get(Calendar.SECOND));

属性

static int HOUR        小时时间
static int MINUTE      分时间
static int SECOND      秒时间
static int DATE        日期的Dte部分
static int MONTH       日期的Month部分 +1
static int YEAR        日期的年部分

字符串的定义(String)

Stringd的创建:有两种方式

  • 静态方式(常用)像是给变量直接赋值一样来使用
String s1 = "sbc";
String s2 = ("abc");
  • 动态方式 动态的内存分配,使用new运算符
String s3 = new String("abc");
String s4 = new String("abc");

这两种方式创建的字符串不是同一种字符串对象,两者有区别:

使用静态方式创建的字符串,在方法区的常量池中只会产生唯一一个字符串对象,使用该方式产生同样一个字符串时,内存中不再开辟另外一块空间,而是两个引用变量指向同一个字符串对象;

字符串的定义

  • 底层由字符串数组来维护
  • 字符串对象一旦被创建,就不能被更改
  • 对字符串重新赋值,实际上是创建了新的字符串
  • 如果字符串中包含了字符串对象,则该内容的地址在堆中;
//字符串文字常量本身是一个String对象
        String str="abc";
        String str1="";//空字符串
        System.out.println(str1);
        
//创建的String对象,表示一个空字符串(“”),空字符与null的区别
//String 对象的内容为空,而null表示String的变量不指向任何的String对象;
        String str2=new String();//空字符串
        System.out.println(str2);
        
        String str3=new String("hello");
        System.out.println(str3);

        String s5 = "hello";
        System.out.println(s5);//hello

        String s6 = s5;
        System.out.println(s6);//hello
        System.out.println(s5==s6);//true
        
            s5="worid";
        System.out.println(s5);//worid
        System.out.println(s5==s6);//false

//(+)连接操作符,可以将其他各种类型的数据类型转换成字符串,并前后连接成新的字符串
String str1 = "hello";
String str2 = "world";
String str = str1+str2;//str为"hello world"

        //如果字符串中包含了字符串对象,则该内容的地址在堆中
        String s7 = "tom";
        String s8="t"+"om";
        System.out.println(s7==s8);//true
        
        String s9 = "jack";
        String s10 = "ja";
        System.out.println(s9==s10+"ck");//false
//生成空字符串的方法
String a[] = new String[5];
  for(int i=0;i<5;a[i++]=" "){
    System.out.println(a[i]);
}
        
String b[] = {"","","","",""};

字符串的方法

字符串也存在下标 ,第一个字符下标为0
        String str="Tinking in Java";
        
        charAt(int index) -char 根据参数下标返回对应的字符
        System.out.println(str.charAt(7));
        
        length() -int 获取字符串长度
        System.out.println(str.length());
        
        concat(String s) -String 将参数字符串连接到原字符串的末尾 
        得到一个新的字符串 对原字符串的内容没有影响
        System.out.println(str.concat(" hello"));
        
         * Tinking in Java  
         * 子串:in,ava
         
        endsWith(String s) -boolean 判断当前字符串是否以参数(子串)为后缀
        System.out.println(str.endsWith("Java"));
        
        //equals(Object obj) - boolean 判断字符串与参数内容是否相同
        System.out.println(str.equals("abc"));
        
        indexOf(String s) -int 返回参数字符串在原字符串中第一次出现的位置
        位置以参数字符串中第一个字符为准
        如果不存在参数字符串 则返回 -1
        System.out.println(str.indexOf("in"));
        
        System.out.println(str.indexOf("abc"));
        
        indexOf(String s, int i) - int 
        从指定下标开始查找,参数字符串在原字符串中第一次出现的位置
        System.out.println(str.indexOf("in", 2));
        
        lastIndexOf(String s) -int 返回参数字符串在原字符串中最后一次出现的位置
        System.out.println(str.lastIndexOf("in"));
        
        isEmpty() -boolean 判断当前字符串是否为空字符串(长度为0) 是 -true , 不是 -false
        System.out.println(str.isEmpty());
        
        replace(String old, String new) - String
        将原字符串中old部分 替换 new 得到一个新字符串 
        System.out.println(str.replace("in", "***"));
        
        contains(String s) -boolean 判断参数是否为原字符串的子串
        System.out.println(str.contains("Jaa"));
        
        startsWith(String str) - boolean 判断原字符串是否以参数字符串为前缀
        System.out.println(str.startsWith("in"));
        
        substring(int index) -String 从给定下标的位置开始截取原字符串 得到一个新的子串
        System.out.println(str.substring(1));
        
        substring(int begin, int end) -String 从给定下标的范围截取原字符串 得到一个新的子串
        System.out.println(str.substring(1, 5));
        
        toCharArray() - char[] 将字符串转换为字符数组
        String类的内部是由一个final修饰的char[]维护的
        char[] ch=str.toCharArray();
        
        toLowerCase() -String 将原字符串中的字母转换为全小写
        System.out.println(str.toLowerCase());
        
        toLowerCase() -String 将原字符串中的字母转换为全大写
        System.out.println(str.toUpperCase());
        
        String str1=" hello world    ";
        String str2="hello world";
        System.out.println(str1.equals(str2));
        
        System.out.println(str1);
        
        trim() -String 将原字符串两端的空白字符消除掉
        System.out.println(str1.trim());

intern()  字符串对象调用该方法后,地址会指向字符串常量池中已存在的地址,但不会永久改变对象的地址,只在方法调用时有效;

字符串内存图

image

String类和StringBuilder类的比较;
  • Java中定义了String与StringBuffer两个类来封装对字符串的各种操作
  • String 类与StringBuffer类都被放到了java,lang包中

两者的主要区别在于:
String类对象中的内容初始值不可以改变
StringBuffer类对象中的内容可以改变

StringBuffer和StringBuilder
  • 可以使用StringBuffer来对字符串的内容进行动态操作,不会产生额外的对象;

StringBuffer类没有重写Object类中的equals()所以使用equals()比较两个StringBuffer对象时比较的仍然是地址

StringBuffer s1 = new StringBuffer("abc");
StringBuffer s2 = new StringBuffer("abc");
    System.out.println(s1==s2);//false
    System.out.println(s1.equals(s2));//false
//转换字符串比较 toSreing();
  System.out.println(s1.toString().equals(s2.toString()));//true

方法

//int length(); 返回长(字符数)
例:
StringBuffer s = new StringBuffer("abcef");
  System.out.println(s.length());//6

//StringBuffer reverse();将此字符串用其反转形式取代
 StringBuffer s = new StringBuffer("abcef");
  System.out.println(s.reverse());//fecba

//StringBuffer delete(int begin,int end) 将除次序列的子字符串的字符
StringBuffer s = new StringBuffer("abcdef");
  System.out.println(s.delete(1, 3));//adef

//String toString 将StringBuffer对象转换成相应的String
....

//StringBuffer append(String str) 将指定的字符串追加到此字符序列
StringBuffer s = new StringBuffer("abcef");
  System.out.println(s.append("gh"));//abcefgh

//StringBuffer insert(int index,int str)将字符串插入此字符串序列中
StringBuffer s = new StringBuffer("abcef");
  System.out.println(s.insert(0,"123"));//123abcef
StringBuffer和StringBuilder都是长度可变的字符串,两者的操作基本相同

区别:
StringBuffer 类是线程安全的
StringBuilder 类是线程不安全的
StringBuffer在JDK1.0中就有,而StringBuilder是在JDK5.0后在出现

你可能感兴趣的:(java基础)