java笔记

命名规则

  1. 变量名和方法名还有类成员变量都采用首字母小写和驼峰命名规则

    即snoName即除了首字母小写,后面的每一个单词的首字母都要大写。

    1. 常量建议全部大写
    2. 类名采用首字母大写和驼峰原则。

变量与其作用域

一、变量的格式

​ 定义:变量是内存当中存储数据最基本的单元,将数据(字面量)放到内存当中,给这块内存空 间起一个名字,这就是变量。所以变量就是内存当中的一块空间,这块空间有名字、有类型、 有值,这也是变量必须具备的三要素。

​ 数据类型 变量名 =值;

二、变量的分类

​ 变量根据声明的位置不同可以分为:局部变量和成员变量。在方法体当中声明的变量以及 方法的每一个参数都是局部变量。在方法体外,类体内声明的变量称为成员变量,成员变量声 明时如果使用 static 关键字修饰的为静态成员变量(简称静态变量),如果没有 static 关键字 修饰则称为实例成员变量(简称实例变量),

​ 例:

public class VarTest06 {
int x = 20; // 实例变量
static int y = 200; // 静态变量
public static void sum(int a, int b){ // 局部变量 a 和 b
int firstNum = 100; // 局部变量
}
}

数据类型

一、数据类型的作用

​ 知道JVM在运行程序的时候给数据分配多大的内存空间。

二、数据类型的分类

​ 字节:一个字节=8位,1KB=1024B,1MB=1024KB,1GB=1024MB

1.基本数据类型

​ 四大类八小种

1.整数:

​ byte:一个字节,-128-127。 默认0

​ short:两个字节,-32768-32767 默认0

​ int:四个字节,-2147483648-2147483647 默认0

​ long:八个字节,后面加L 例子:long num2 =3L 默认0

2.浮点型:

​ 只要是浮点型的字面量,默认会被当 做 double 类型来处理,如果想让程序将其当做 float 类型来处理,需要在字面量后面添加 f/F。

​ float:四个字节,后面加F 例子:float num=3.12F,有效位数:6~7位 默认0.0

​ 例子:float c1=3.14F;

​ double:八个字节,有效位数15位 默认0.0

3.布尔型:

​ boolean:一个字节,false或者TRUE。 默认FALSE

​ 没有其他值,不包括 1 和 0。boolean 类型在开发中主要使用在逻辑判断方面。

4.字符型:

​ char:两个字节。 0~65535 默认\u0000

​ short和char所表示的种类的数目是一样的,只不过char可以表示更大的正数,因为char没有负数。

​ 成员变量没有手动赋值的时候系统会自动赋值,局部变量不会。

​ 八种基本类型的默认值一切都向0看齐。

2.引用数据类型 默认为null

接口
数组
字符串

三、转义字符"\"

  1. 转义字符指用一些普通的字符组合代表一些特殊 的字符,由于组合用的字符改变了原意,称为转义字符。

  2. Java 中的转义字符以 \ 开始,常见的 转义字符有:\t、\n、\u、\、’,",其中\t 代表制表符,\n 是换行符,\表示一个普通的\字符, ‘表示一个普通的’,“表示一个普通的”。

四、强制转换

  1. 当一个整数型的字面量没有超出 byte,short,char 的取值范 围,可以将该字面量直接赋值给byte,short,char 类型的变量,如果超出范围则需要添加强制类 型转换符。

  2. 例子:

    char c3 = 65536;//错误
    
    
  3. 第一,Java 中的整数型字面量有四种表示方式,但最常用的还是十进制;

  4. 第二,整数型字面量被当做 int 处理,如果想当做 long 处理, 需要在后面添加 L 或 l;

  5. 第三,小容量转换为大容量被称为自动类型转换,容量从小到大的排序为:byte < short(char) < int < long < float < double;例子:

    int  c1=3;
    double c2;
    c2=c1;
    
  6. 第四,大容量转换成小容量称为强制类型转换,强转时需要添加强制类型转换符,但要注意强转可能损失精度;例子:double c1=3.14; int c2; c2=(int)c1;

  7. 第五,当整数型字面量没有超出byte、short、char 的取值范围,可直接赋值。

  8. 八种基本数据类型中,除 boolean 类型不能转换,剩下七种类型之间都可以进行转换。

  9. int a=3;
    byte c=1;
    c=a+1;//错误这是因为 byte 类型的 a 和 int 类型的 4 求和,结果为 int 类型,并且对于以上代码来说 a 是一个变量,变量就是一个不确定的值,所以编译器会认为 a + 4 可能会超出 byte 取值范围,所以编译报错了。
    

字符编码

一、定义:

​ 为了让计算机可以表示现实中的文字,人为的提前制定好文字与二进制之间的对照关系就是字节编码。

二、ASCLL码(最初的时候美国标准协会制定了 ASCII 码,ASCII(American Standard Code for Information Interchange:美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现 代英语和其他西欧语言。)采用一个字节的编码。

1. 常用:A->65 	a->97		0->48
2. 文字变成二进制就是解码,二进制变成文字就是编码,解码和编码采用同一种方式。
3. 支持简体中文的编码方式:GB2312<GBK,GB18030
4. 通用的编码方式为 ISO-8859-1。
5. Java 采用 unicode 编码,目前在实际的开发中大部分团队都会 选择 UTF-8 的编码方式。所以JAVA标识符支持中文。

运算符

一、运算符的定义

  1. 运算符是指对操作数的运算方式。

  2. 算术运算符、关系运算符、逻辑运算符、赋值运算符、 条件运算符、字符串连接运算符。

  3. 运算符是指对操作数的运算方式。组成表达式的 Java 操作符有很多种(什么是操作数和操 作符,例如 1+2,其中 1 和 2 都是操作数,+是操作符,操作符和操作数联合起来构成表达式)。

  4. 运算符按照其要求的操作数数目来分,可以有单目运算符(1 个操作数)、双目运算符(2 个 操作数)和三目运算符(3 个操作数)。

  5. 运算符按其功能来分,有算术运算符、赋值运算符、 关系运算符、逻辑运算符、位运算符、条件运算符、字符串连接运算符和其他运算符。

二、运算符的分类

1、算数运算符

​ 算术运算符包括:+(两个数字求和)、-(两个数字相减)、*(两个数字乘积)、/(两 个数字相除)、%(两个数字取模或者求余)、++(单目运算符,对数字自加 1)、–(单目 运算符,对数字自减 1)。

重点讲解++和–

  1. java 语法中又有这样的规定,++出现在变量后,会先进行赋值后加一。例子:

    int c2=2;
    c2=c2++;
    System.out.println(c2);//结果为2
    
  2. java 语法中又有这样的规定,++出现在变量前,会先进行加一。例子:

    int c2=2;
    c2=++c2;
    System.out.println(c2);//结果为3
    
  3. 两个 int 类型数据进行数学运算之后的结果一定 是 int 类型。

2、关系运算符

关系运算符主要是完成数据和数据之间的比较

  1. <、>、<=、>=、==、!=。

  2. 任何一个关系运算符的运算结果都是布尔类型,最后的结果不是 true 就是 false,没有其他值。

3、逻辑运算符

逻辑运算符主要包括逻辑与(&),逻辑或(|),逻辑异或(^),短路与(&&),短路 或(||)。

  1. 所有逻辑运算符的特点是操作数都是布尔类型,并且最终的运算结果也是布尔类型。

  2. 短路原则:与(&&)在左边的表达式结果为 false 的时候右边的表 达式则不再执行。

  3. 短路与(&&)左边为FALSE时右边不再执行,&左边为FALSE时右边还是会执行。例子:

    int c1=1;
    int c2=2;
    System.out.println(1>2&&c1>++c2);
    System.out.println(c2);//结果c2=2
    =================================
    int c1=1;
    int c2=2;
    System.out.println(1>2&c1>++c2);
    System.out.println(c2);//结果c2=3
    

短路||与|同样如此。

4、赋值运算符

赋值运算符目前也是只需要掌握=、+=、-=、*=、/=、%=。

  1. x += 1; //等同于 x = x + 1, y -= 1; //等同于 y = y - 1, z *= 2; //等同于 z = z * 2, m /= 3; //等同于 m = m / 3, n %= 3; //等同于 n = n % 3,
  2. 对于扩展类的赋值运算符在运算的过程中不会改变运算的结果类型,也就是说 byte b = 100; b += 1000就是:b = (byte)(b + 1000);b 变量最初是 byte 型,最后的运算结果还是一个 byte 类型。
  3. 以后在使用扩展类赋值运算符的时候要谨 慎,不小心就会精度损失的。例子:byte b = 100; b += 1000。就会造成精度的丢失。运行结果为76.

5、条件运算符

(1)、定义:

条件运算符属于三目运算符,它的语法结构是:布尔表达式?表达式 1:表达式 2。例子:int k=false?1:2;

(2)、运行原理

它的运行原理是这样的,先判断布尔表达式的结果是 true 还是 false,如果是 true,则选择表达式 1 的结 果作为整个表达式的结果,反之则选择表达式 2 的结果作为整个表达式的结果。

6、字符串连接运算符

(1)、定义:

实际上“+”运算符在 java 语言中有两个作用,作用一是对数字进行求和运算,作用二就 是字符串连接运算。当“+”运算的时候,两边的操作数都是数字的话,一定会进行求和运算,只 要其中有一个操作数是字符串类型,那么一定会进行字符串拼接运算,字符串拼接之后的结果 还是字符串类型。

(2)、注意

当一个表达式当中有多个“+”,并且在没有小括号的前提 下,遵循自左向右的顺序依次执行。例子:

int a=1;
int b=2;
System.out.println(a + " + " + b + " = " + a + b);//字符串连接运算1 + 2 = 12
System.out.println(a + " + " + b + " = " + (a + b));//加法运算1 + 2 = 3

将变量放到字符串当中进行拼接,可以这样 做:

在拼接的位置添加一个英文双引号,在双引 号中间添加两个加号,把字符串变量放到两个加号中间。

例子:System.out.println(“欢迎”+name+“回来!”);

总结:

1.int k = 10;

System.out.println(k++);

System.out.println(++k);

执行顺序:1、先输出k的值为10. 2、然后k自增为11, 3、k自增为12, 4、输出k的值。

控制语句

一、定义:

​ 控制语句即用来实现对程序流程的选择、循环、转向 和返回等进行控制。

二、分类

1、选择语句

​ 选择语句又称为分支语句,它通过对给定的条件进行判断,从而决定执行两个或多个分支 中的哪一支。在 Java 语言中选择语句主要提供了两个, 一个是 if 语句,另一个则是 switch 语句。

1.1、if语句

​ 四种:

if (1>2){
    System.out.println("第一");
}
else {
    System.out.println("第一个");
}
================================
if (1>2){
           System.out.println("第一");
       }
else if (2>1){
           System.out.println("第二个");
       }
else {
           System.out.println("第三个");
       }
=================================
 if (1>2){
           System.out.println("第一");
       }
       else if (2>3){
           System.out.println("第二个");
       }
       else if(3<4){
           System.out.println("第三个");
       }
    }
==================================
 if (1>2){
           System.out.println("第一");
       }
       else if (2>3){
           System.out.println("第二个");
       }
       else if(3>4){
           System.out.println("第三个");
       }
       else {
           System.out.println("disige");
       }
    }

注意:虽然 java 规定当分支中只有一条 java 语句的话大括号可以省略,但是为了程序具有很强的 可读性,所以建议在实际开发中还是不要省略大括号会比较好。

1.2、switch

​ switch 语句和 if 语句一样,都属于选择语句(分支语句)。

  1. switch 语句后面的小括号“()”当中都可以出现什么,switch 语句除了支持 int 类型之外,还支持 String 类型。

    例子:

    java.util.Scanner s=new  java.util.Scanner(System.in);
    System.out.println("请输入数值");
    String name=s.next();
    switch (name){
        case "chen":
            System.out.println("陈绪杰");
        case "1":
            System.out.println("chenxujei 2");
        default:
            System.out.println("陈绪杰好人");
    
    }
    

2.switch 虽然只能探测 int 类型,但是也可以将 byte,short,char 类型放到小括号当中,因 为这些类型会自动转换成 int 类型(小容量向大容量转换称为自动类型转换)。

3.switch 语句当中当某个分支匹配成功,则开始执行此分支当中的 java 语句,当遇到当 前分支中的“break;”语句,switch 语句就结束了,但是如果当前分支中没有“break;”语句, 则会发生 case 穿透现象,也就是说下一个分支也不再进行匹配,直接进入下一个分支执行, 直到遇到“break;”为止。即找一个程序的入口。没有break就一直往下执行。

4.switch 语句中 case 要求的是常量,一般是不能进行逻辑判断的。

2、循环语句

2.1、for

​ 格式:for(表达式;布尔表达式;表达式);

例子:

int i;
for (i=0;i<10;i++)
    System.out.print(i);

1.对于 for 循环来说,初始化表达式、布尔表达式、更新表达式都不是必须的,当布尔表达 式缺失的时候,没有条件控制的前提下,会形成死循环。但是分号是不能少的。

例子:for (;😉;

2.关于 for 循环的使用我们还需要注意初始化变量的作用域,在 for 循环当中声明的变量只在 for 循环中有效,当 for 循环结束之后,初始化变量的内存就释放了/消失了,所以在 for 循环之 外是无法访问该变量的。

int i;
for (i=0;i<10;i++){
    System.out.print(i);
}//作用域在整个方法体内

        for ( int i=0;i<10;i++){
            System.out.print(i);
        }//作用域在FRO语句内
2.2、while

​ 循环语句除了 for 循环之外还有 while 和 do…while,循环体执行次数为 0~N 次

​ 格式:while(布尔表达式){循环体}

​ 执行原理:先 判断布尔表达式的结果,如果是 true,则执行循环体,循环体结束之后,再次判断布尔表达式 的结果,如果是 true,再执行循环体,循环体结束之后,再判断布尔表达式的结果,直到结果 为 false 的时候,while 循环结束。如果起初第一次判断布尔表达式结果的时候就是 false,那么 while 循环体执行次数就为 0 了。

2.3、do…while

​ do…while 循环是 while 循环的变形,它们的区别在于 do…while 循环可以保证循环体执行次 数至少为 1 次,也就是说 do…while 循环的循环体执行次数是 1~N 次。

​ 格式:

int i=0;
do {
    System.out.println(i);
    i++;
}while (i<10);//一定要注意while后面的分号,不能忘了写

执行原理:而 do…while 循环最先执行的不是条件判断,它会先执行循 环体,然后再进行条件判断,这样就可以保证循环体至少执行一次喽!

3、转向语句

转向语句用于实现循环执行过程中程序流程的跳转,在Java中转向语句有 break和 continue 语句。当然,还包括其它的,例如 return 语句。

3.1、break

1.用途:可以用作于swith和循环语句中。break 语句重点是使用在循环语句当中,用来终止/跳出循环。

2.break 语句默认情况下只能终止离它“最近”的“一层”循环。

3.我们可以得知当多层循环嵌套的时候,可以给每个循环设置标识, 例如:first:for…、second:for…,当某个条件成立时,想终止指定循环的话,可以这样做:break first;或者 break second;,这样指定的循环就结束了。

例子:

first:for (int i=1;i<=9;i++){
    for (int j = 1;j <=i;j++){
        System.out.print(i+"*"+j+"="+i*j+"  ");
        break first;
    }
    System.out.println();
}
3.2、continue

1.用途:只用作与循环语句中。

2.continue 语句则是用来终止当前本次循 环,直接进入下一次循环继续执行。作用于循环语句中的条件判断出。

3.当“continue;”语句执行的时候,当前本次循环剩下的代码就不再执行了 (不再执行下面的输出语句)。

4.总之,break 用来终止循环,continue 用来中断当前本次循环,直接进入下一次循环继续执行。

方法

一、定义:

1.(多使用,不要代码都在main方法中写,方便别人使用。因为方法是一个独立的功能,可以重复使用。)

2、方法是一段可以完成某个特定功能的并且可以被重复利用 的代码片段。

二、格式:

【修饰符列表】 返回值类型 方法名 (形式参数){

​ 方法体

}

三、分开解释:

3.1、修饰符列表:

此项是可选项,不是必须的,目前统一写成 public static。

3.2、返回值类型

1、定义:非常重要,在你写方法之前你就要只要此方法应该完成什么功能,返回什么类型的数据。

此项可以是 java 语言当中任何一种数据类型,包括基本数据类型,也包 括所有的引用数据类型,当然,如果一个方法执行结束之后不准备返回任何数据,则返回值类 型必须写 void。返回值类型例如:byte,short,int,long,float,double,boolean,char,String,void 等。

2、当有返回值的时候,即返回值类型不是void时必须写return语句。当返回值类型为void时也可以使用return语句。

3、也就是说当一个方法的返回值类型是 void 的时候,方 法体当中允许出现“return;”语句(注意:不允许出现“return 值;”),这个语法的作用主要 是用来终止方法的执行。

例子:public static void method2(){ return; }对的。 public static void method2(){ return 10; }错的。

3.3、方法名

1、定义:

此项需要是合法的标识符,开发规范中要求方法名首字母小写,后面每个单 词首字母大写,遵循驼峰命名方式,见名知意,例如:login、getUsername、findAllUser 等。

3.4、形式参数

1、定义:(数据类型 变量名,数据类型 变量名。。。。。。)

此项又被称为形参,其实每一个形参都是“局部变量”, 形参的个数为 0~N 个,如果是多个参数,则采用半角“,”进行分隔,形参中起决定性作用的 是参数的数据类型,参数名就是变量名,变量名是可以修改的,也就是说(int a , int b)也可以写 成(int x , int y)。

3.5、方法体

1、定义:

由一对儿大括号括起来,在形参的后面,这个大括号当中的是实现功能的核心代码,方法体由 java 语句构成,方法体当中的代码只能遵循自上而下的顺序依次逐行执行, 不能跳行执行。

3.6、方法的调用

1、前提是方法的修饰符列表中带有 static 关键字): “类名.方法名(实际参数列表)。当方法写在当前类体中的时候可以不加类名,但是方法写在其他类体中的时候就要使用 类名.方法名()调用。

2、调用的格式:类名.方法名(实际参数)

3、实际参数与形式参数的数据类型要一样,数量要一样。

例子:

long x;
    //long a=1L;
   // long b=2L;
    x=sum(10,20);
    System.out.println(x);
}

public static int sum(int x, int y) {
    if (x > y) {
        return x;
    }else return 0;
    //此程序可以运行,但是会有精度的损失,从int类型到long类型。
2public static void main(String[] args) {
       sum(true,10);
    }

public static void sum(int x, int y) {
        if (x > y) {
            System.out.println("0");
        }else
            System.out.println("1");
    }//此程序就会显示形参和实参的类型不同的错误。

深入return语句

1、同一作用域下,return语句下面不能编写任何代码:

例子:

public static int sum(int x,int y){
    if(x>y){
        return x;
    }//错误错误原因是因为无法确定这个return是不是一定运行,所以是缺少返回语句。
public static int sum(int x,int y){
        if(x>y){
            return x;
        }else {
            return y;
        }//对的

在返回类型为void的方法中使用return语句。主要是为了用来结束当前方法。break只能终止某个循环。

1、方法执行结束之后的返回值我们可以接收,也可以不接收,不 是必须的,但大多数情况下方法的返回值还是需要接收的,要不然就没有意义了,另外方法的返回值在接收的时候采用变量的方式接收,要求这个变量的数据类型和返回值类型一致(当然, 也可以遵守自动类型转换规则)。

例子:

public static void main(String[] args) {
//可以编译也可以正常运行
sumInt(10 , 20);
int retValue = sumInt(100 , 200);
System.out.println("计算结果 = " + retValue);
//编译报错,返回值类型是 int,不能采用 byte 接收
//byte retValue2 = sumInt(1 , 2);
}
public static int sumInt(int a , int b){
return a + b;
}

四、数据结构

1、JVM的内存区:

栈区,方法区,堆区。

2、栈的介绍

栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是:仅允许在表的一端进行插 入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作 进栈、入栈或压栈(push),它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从 一个栈删除元素又称作出栈、退栈或弹栈(pop),它是把栈顶元素删除掉,使其相邻的元素 成为新的栈顶元素。

特性:先入后出。

3、方法的执行与调用

1、类加载器把.class文件加载到方法区(可以重复的代码块)

2、程序开始执行,方法进入栈区。

具体的程序实例:

public class MethodTest {
public static void main(String[] args) {
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1() {
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2() {
System.out.println("m2 begin");
System.out.println("m2 over");
}
}

运行结果:

文字描述:

① 类加载器将 class 文件加载到方法区。

② 开始调用 main方法,在栈内存中给 main方法分配空间,开始执行 main方法,输出”main begin”。

③ 调用 m1()方法,在栈内存中给 m1()方法分配空间,m1()方法处于栈顶,具备活跃权, 输出”m1 begin”。

④ 调用 m2()方法,在栈内存中给 m2()方法分配空间,m2()方法处于栈顶,具备活跃权, 输出”m2 begin”,继续输出”m2 over”。

⑤ m2()方法执行结束,内存释放,弹栈。

⑥ m1()方法这时处于栈顶,具备活跃权,输出”m1 over”。

⑦ m1()方法执行结束,内存释放,弹栈。

⑧ main()方法这时处于栈顶,具备活跃权,输出”main over”。

⑨ main()方法执行结束,内存释放,弹栈。

⑩ 栈空了,程序结束。

4、栈内存主要存储局部变量。

方法重载(OverLoad)

4.1、定义:

1.方法重载(overload)是指在一个类中定义多个同名的方法, 但要求每个方法具有不同的参数的类型或参数的个数。

2.方法重载通常用于创建完成一组任务相 似但参数的类型或参数的个数不同的方法。

3.调用方法时通过传递给它们的不同个数和类型的实参来决定具体使用哪个方法。

4.2、什么时候考虑方法重载

​ 在同一个类当中,如果多个功能是相似的,可以考虑将它们的方法名定义的一致,使用方法重载机制,这样便于程序员的调用,以及代码美观, 但相反,如果两个方法所完成的功能完全不同,那么方法名也一定要不一样,这样才是合理的。

4.3、什么条件满足之后构成方法重载

1.在同一个类当中。

2.方法名相同。

3.参数列表不同:

​ 个数不同算不同。

​ 顺序不同算不同。

​ 类型不同也算不同。

例子:

public class DemoOverLoad {
    public static void main(String[] args) {
        m1(1,2.0);
        m1(1.0,2);
        m1(1);
        m1(1.0F,2.0);

    }
    //顺序不同
    public static void m1(int a,double b){
        System.out.println("1");
    }
    public static void m1(double a,int b){
        System.out.println("2");
    }
    //数量不同
    public static void m1(int a){
        System.out.println("3");
    }
    //类型不同
    public static void m1(float a,double b){
        System.out.println("4");
    }
//    void m1(int a,double b){
//        System.out.println("1");
//    }

}

4.4、方法重载和什么有关系,和什么没有关系

1.方法重载之和方法名以及参数的类型有关系。

2.和有无修饰符列表无关系。

错误的例子:

void m1(int a,double b){
    System.out.println("1");
}
public static void m1(int a,double b){
    System.out.println("2");
}

3.方法重载和返回值类型无关

public static int  m1(int a,double b){
    System.out.println("1");
}
public static void m1(int a,double b){
    System.out.println("2");
}//错误的写法

4.5、方法重载的实际应用

可以自己写一个模块,然后在其它的类中加载这个模块,代码简洁方便。

 public static void main(String[] args) {
        A.m1(1);
        A.m1(2.0F);
        A.m1(2.0);
        A.m1(2L);
        A.m1("陈绪杰");
    }
}
class A{
    public static void m1(int data){
        System.out.println(data);
    }
    public static void m1(double data){
        System.out.println(data);
    }
    public static void m1(float data){
        System.out.println(data);
    }
    public static void m1(byte data){
        System.out.println(data);
    }
    public static void m1(long data){
        System.out.println(data);
    }
    public static void m1(String data){
        System.out.println(data);
    }

递归

1.定义:

1.方法自己调用自己,就是递归。

2.栈内存使用比较多,能不要用就不用。

3.使用递归须谨慎,因为递归在使用的时 候必须有结束条件,没有结束条件就会导致无终止的压栈,栈内存最终必然会溢出,程序因错 误的发生而终止。

4.一个递归程序有的时候存在合法有效的终止条件,但由于递归的太深,在还没有等到条件 成立的时候,栈内存已经发生了溢出,这种情况也是存在的,所以实际开发中我们尽可能使用 循环来代替递归算法。

5.能不用递归尽量不用,能用循环代替的尽可能使用循环。

6.递归的例子,在面试的过程中也经常使用:

//递归算法
public class DemoDigui {
    public static void main(String[] args) {
       int n=4;

       int retvalue=sum1(n);
        System.out.println(retvalue);
    }
    //利用递归计算1~N的累加和
    public static int sum(int a){
        if (a==1){
            return a;
        }
        a=a+sum(a-1);
        return a;
    }
    //利用递归计算N的阶乘,面试题经常考
    public static int sum1(int n){
        int a;
        if (n==1)
            return 1;
        return n*sum1(n-1);


    }
}

总结

  1. 方法体当中的代码必须遵循自上而下的顺序依次逐行执行,当前行代码不结束,下一行代码 是绝对不会执行的。
  2. 一定要理解栈数据结构的原理是遵循先进后出、后进先出 原则,每当方法调用时分配空间,此时“进”栈,每当方法执行结束时释放空间,此时“出” 栈。
  3. 永远都是最后执行的方法最先结束,最先执行的方法最后结束。

面向对象与面向过程

一、面向过程

1、定义:

1.过程为中心的编程思想。没有独立体的概念。简称OP。

2.注重的是因果关系。

3.以面向过程的编程方式关注点不 在“事物”上,而是做这件事分几步,先做什么,后做什么。

2、优缺点:

优点:对于业务逻辑开发比较简单的程序,可以达到快速开发,前期投入成本较低

缺点:开发过程中难以解决非常复杂的业务逻辑,没有独立体的概念,无法达到组件复用。

耦合程度高,扩展性比较低。

二、面向对象

1、定义:

以对象为中心的编程思想。在描述事物的过程中,通常把一个事物描述成不同的单元。简称 OO。

2、优缺点:

优点:耦合度比较低,扩展性高。复用性比较高。

面向对象将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

缺点:前期投入成本比较高,需要进行独立体的抽取,大量的系统分析与设计。

3、开发周期:

使用面向对象编程思想开发系统,在现代开发中会将面向对象贯穿整个过程。

1、OOA:面向对象分析(Object-Oriented Analysis)

2、OOD:面向对象设计(Object-Oriented Design)

3、OOP:面向对象编程(Object-Oriented Programming)

4、面向对象的三大特征:

封装、继承、多态

三、区别

1、c语言是面向过程的,C++是面向对象的,JAVA纯面向对象的。

2、面向对象更符合人的思维方式。

3、面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

类和对象

一、类

1、定义:

类是现实世界当中具有共同特征的事物进行抽象形成的模板概念

2、组成:

状态(属性)+动作(方法)

属性:属性在实例化的时候JVM自动给其赋值

例子:以从对象到类称为抽象。

​ 对象的属性以变量形式存在,就是成员变量中的实例对象。

​ 为什么属性用成员变量表示:因为要存储数据,数据就是要反映对象的属性。数据就要存在变量里面。

访问:为什么是实例变量呢,实例变量就是对象级别的变量,这样的变量要求必须先存在对象,通过对象才能访问

3、类和对象的关系:

1、类到对象->类可以创建对象,对象又被称为实例(instance),这个过程也可以称为实例化.

2、对象到类->从对象到类称为抽象。

4、类的定义:

[修饰符列表] class 类名(){

​ 类体 = 属性 + 方法

}

例子:设计银行账户类,每个账户都有账号、密码、余额等信息。

public class Account {
    String actno;
    String password;
    double balance;
}

面试:

把一类的东西进行归类,形成一个模板。就是类。

软件的开发过程:

1、程序员先观察现实世界,从现实世界当中寻找对象

2、寻找了很多对象之后,发现所有的对象都有一个共同的特征

3、程序员在脑海中形成了一个类(模板)

4、程序员通过java代码来实现一个类。

5、java程序中有了类的定义

6、通过类创建对象

7、有了对象,可以让对象直接协作起来形成一个系统。

二、对象

1、定义:

对象是实际存在的个体,现实世界中实际存在的。

2、格式:

​ 类名 引用名=new 类名(); new 类名()例子:Student s=new Student();

3、对象的使用:

​ 1、引用名.实例变量名;例子:s.mo 2、数据类型 局部变量名=引用名.实例变量名;例子:int s1=s.mo;

三、空指针异常

java.lang.NullPointerException 被称为空指针异常,在 java 编程当中属于很常见的异常。

空引用访问实例相关的数据一定会出现空指针异常。

例子:

//创建User对象
User u=new User();
u=null;

当 u= null 执行之后表示“引用 ball”不再保存 java 对象的内存地址,换句话说通过 u引用已经无法找到堆内存当中的 java 对象了,对于程序 来说这个时候就没有办法正常访问了,这种情况下就会发生空指针异常。因为此时u里面存储的不是对象的内存地址了,里面是空的。所以会发生空指针异常。

四、类里面的东西总结:

class{

​ 静态代码块;在类加载的时候执行

static{

}

​ 实例代码块,在对象创建的时候执行

public Test05() {
    System.out.println("实例代码举哀");
}

{
    System.out.println("cehnxujie ");
}

​ 静态变量

​ static int a;

​ 类名.a

​ 实例变量

​ int a;

​ 引用.a;

​ 静态方法

public static int sum()

{

}

调用:

类名.方法名

​ 实例方法

public int sum()

{

}

调用:

引用.方法名

封装

1、定义:

​ 利用抽象数据类型将数 据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据 类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节,但可以通过该对象对外提供的接口来访问该对象。

面向对象的三大特征之一。

2、优点:

1、对于事物来说,看不到事物复杂的一面,只能看到事物简单的一面。

2、复杂性封装,对外提供简单的操作入口,照相机、遥控器都是封装的例子。

3、封装之后才会形成真正的“对象”,真正的“独立体”。

4、封装之后程序可以重复使用。降低程序的耦合度,提高程序的扩展性。

5、封装过后程序更加的安全。对于事物本身,提高了安全性。

3、封装的步骤:

1、第一步就是将应该隐藏的数据隐藏起来。所有属性私有化,使用private关键字进行修饰。private 修饰的数据表示私有的,私有的数据只能在本类当中访问。

2、对外提供公开的访问 入口,让外部程序统一通过这个入口去访问数据,我们可以在这个入口处设立关卡,进行安全 控制,这样对象内部的数据就安全了。

​ 对外提供两个公开的方法,分别是set方法和get方法

​ 想读取属性,调用get方法。

​ 命名规范:public 数据类型 get属性名(第一个单词大写)(){

​ return 变量名;

​ }

​ 想修改属性,调用set方法。

命名规范:public void set变量名2(第一个单词大写)(数据类型 变量名2){

​ this.变量名2=变量名2;

​ }

例子:

public String getCity() {
    return city;
}
public void setCity(String city) {
        this.city = city;
}

另外 set 方法和 get 方法都不带 static 关键字不带 static 关键字的方法称 为实例方法,这些方法调用的时候需要先创建对象,然后通过“引用”去调用这些方法,实例 方法不能直接采用“类名”的方式调用。)

构造方法

1、定义:

构造方法是类中特殊的方法,通过调用构造方法来完成对象的创建,以及对象属性的初始化操作。

2、作用:

​ 1、创建对象

​ 2、给实例对象赋值

​ 3、所有的构造方法在执行过程中没有给对象的属性手动赋值,系统则自动赋默认值,实际上大部分情况下我们需要在构造方法中手动的给属性赋值,这本来就是构造方法的主要的职责。

例子:

public class Test01 {
    public static void main(String[] args) {
        new FangFa();
        FangFa f1=new FangFa(20);

        System.out.println(f1.getNo());
        FangFa f2=new FangFa(10,"陈绪杰");
        System.out.println("学号:"+f2.getNo()+"姓名;"+f2.getName());

    }
}
class FangFa {
    private int no;
    private String name;
    //构造无参数的方法
    public FangFa(){
        System.out.println("无参数的构造方法");
    }
    //构造有参数的构造方法
    public FangFa(int a){
        no=a;
        System.out.println("有参数的构造方法");
    }
    public FangFa(int a,String b){
        no=a;
        name=b;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
输出:无参数的构造方法
有参数的构造方法
20
学号:10姓名;陈绪杰

3、定义格式:

​ [修饰符列表] 构造方法名(形式参数){

​ 构造方法体

}

例子:

//有参数的构造方法
public FangFa(int a){
    no=a;
    System.out.println("有参数的构造方法");
}
//构造无参数的方法
    public FangFa(){
        System.out.println("无参数的构造方法");
    }

调用:new 构造方法名(实际参数列表);

例子;

new FangFa();
FangFa f1=new FangFa(20);
FangFa f2=new FangFa(10,"陈绪杰");

4、注意:

1、构造方法名和类名一致。

2、构造方法用来创建对象,以及完成属性初始化操作。

3、构造方法返回值类型不需要写,写上就报错,包括 void 也不能写。

4、 构造方法的返回值类型实际上是当前类的类型。

5、一个类中可以定义多个构造方法,这些构造方法构成方法重载。

6、带static的方法调用的时候是 类名.方法名(实际参数) 不带static的方法调用的时候是 引用.方法名(实际参数)。

/**
 * 知识点:
 * 1、有无static修饰符的方法的调用
 * 2、当一个类中没有构造方法的时候默认有一个无参数的构造方法
 */
public class Test02 {
    public static void main(String[] args) {
        dosum();
        //系统默认给构造的一个无参数的构造方法
        Test02 t1=new Test02();
        t1.dum();
    }
    public static void dosum(){
        System.out.println("有static修饰符的方法");
    }
    public void dum(){
        System.out.println("无static修饰符的方法");

    }
}

7、当一个类没有显示的定义任何构造方法的时候,系统默认提供无参数构造方法,当显示的定义构造方法之后,系统则不再提供无参数构造方法。

8、构造方法的作用是专门用来创建对象同时给属性赋值的,它的 语法很简单,比普通方法还要简单,因为构造方法名和类名一致,还不需要写返回值类型,使 用 new 就可以调用了。在一个类当中可以同时定义多个构造方法,它们之间构成重载关系。 这样就做到了在 java 中你想要什么就 new什么,每一次 new都会在堆内存中创建对象,并且对象内部的实例变量被初始化了。

五、总结:

大多数方法都定义为实例方法,一般一个行为或者一个动作在发生的时候都需要对象的参与。

但是也有例外,当构造工具类的时候就不需要定义实例方法,为了就是方便调用和方便编程。

参数传递

1、这个过程就 是赋值的过程,参数传递和“赋值规则”完全相同,只不过参数传递在代码上看不见“=”运 算符。

2、java方法中的参数传递永远传递的都是变量里面的那个值,有的值是字面值,有的值是另一个java对象中的内存地址。

5、参数的传递就是值的传递。

User u=new User();

User u2=new User();

u2=u,将u里面的内存地址传给U2,他们指向的是堆内存中的同一个java对象。

例子:

/**
 * 1、首先s里面存放的是STUDENT对象的地址
 * 2、 add(s);s1=s所以现在s1里面存放的就是Student对象的地址
 * 总结:参数传递传递的只是值,有的参数传递的是字面值,有的参数传递的是对象的地址。
 */
public class Test03 {
    public static void main(String[] args) {
        Student s=new Student(20);
        add(s);
        System.out.println("2:"+s.no);

    }
    public static void add(Student s1){
        s1.no++;
        System.out.println("1:"+s1.no);
    }
}
class Student{
    int no;

    public Student() {
    }

    public Student(int no) {
        this.no = no;
    }
}
运行结果:
1;21
2:21

例子2:

public class Test04 {
    public static void main(String[] args) {
        int i=10;
        dosum(i);
        System.out.println("2:"+i);
    }
    public static void dosum(int a){
        a++;
        System.out.println("1:"+a);
    }
}
运行结果:
1:11
2:10

访问控制权限

一、访问控制权限都有哪些?

1、private 私有

2、public 公开

3、protected 受保护

4、 默认

二、控制的范围

1、private 表示私有的,只能在本类中访问

2、public 表示公开的,在任何位置都可以访问

3、“默认”表示只能在本类,以及同包下访问。

4、protected表示只能在本类、同包、子类中访问。

访问控制修饰符 本类 同包 子类 任意位置

​ public 可以 可以 可以 可以
​ protected 可以 可以 可以 不行
​ 默认 可以 可以 不行 不行
​ private 可以 不行 不行 不行

​ 这个不要死记硬背,自己下去之后编写代码自己测试。

​ 范围从大到小排序:public > protected > 默认 > private

三、访问控制权限修饰符可以修饰什么?

​ 1、属性(4个都能用)
​ 2、方法(4个都能用)
​ 3、类(public和默认能用,其它不行。)
​ 4、接口(public和默认能用,其它不行。)
​ …

this

一、定义:

​ 1.1、this是一个关键字,是一个引用,保存内存地址指向自身。每创建一个对象就有一个this。
​ 1.2、this可以使用在实例方法中,也可以使用在构造方法中。
​ 1.3、this出现在实例方法中其实代表的是当前对象。
​ 1.4、this不能使用在静态方法中。
​ 1.5、this() 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用。

二、重点:

实例变量的访问一定要有对象。

主要是两个方法,一个加static的一个不加static的

例子:

//带有static的方法不能使用this
    public static void dosum() {
        CeShi w=new CeShi();
        w.name="陈绪杰2";
        System.out.println(w.name);
        //System.out.println(this.name);错误
    }
    //不带有static的方法可以使用this
    public void sohello() {
        System.out.println(this.name);
    }

三、调用实例方法:

调用实例方法一定要有对象的存在

带有static的方法不能直接访问实例变量和实例方法、

因为实例对象和实例方法都需要有对象的存在。

例子:

public class Test02 {
    //实例变量
    String name="陈绪杰2222";
    //实例方法
    public void dosum(){
        System.out.println("陈绪杰");
        System.out.println(name);
        System.out.println(this.name);
    }
    public static void main(String[] args) {
        //带有static的方法,不创建对象不能调用实例变量和实例方法,但是main函数是个特殊的。
        //创建变量
        Test02 tt=new Test02();
        System.out.println(tt.name);
        tt.dosum();
    }
}

四、this不能省的情况:

this. 大部分情况下可以省略,但是用来区分局部变量和实例变量的时候不能省略。

例子:

public class Test03 {
    int id;
    public void su(int a){
        id=a;//因为实例对象有对象,所以这里的this可以省略
        System.out.println(id);

    }
    public void su2(int id){
        this.id=id;//这里的this就不可以省略,用于区分实例变量与局部变量
        System.out.println(this.id);
    }

    public static void main(String[] args) {
    	//创建对象
        Test03 tt=new Test03();
        tt.su(20);
        tt.su2(30);
    }
}

写一个类步骤

1、私有化属性

2、创建get,set方法

3、构造无参的和有参的方法

4、重写tostring方法

五、this的使用地方

1、出现在实例方法当中,代表当前对象

格式: this.实例变量

2、出现在构造方法当中

this(实参列表) 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用。

例子:

//构造有参的方法
    public This04(int no, String name) {
        this.no = no;
        this.name = name;
    }
//构造无参的方法与上面的方法出现代码重复,所以使用下面的调用构造方法
    public This04() {
        //new This04(10,"chenxujie");//这种会在创建一个对象最终输出的值为空
        this(10,"chenxujie");//正确的写法
    }

static

1、定义:

static 是 java 语言中的关键字,表示“静态的”,它可以用来修饰变量、方法、代码块等。

2、存储地方

JVM的方法区内存,静态变量在类加载时初始化。

3、作用

在实际的开发中,“工具类”当中的方法一般定义为静态方法,因为工具类就是为 了方便大家的使用,将方法定义为静态方法,比较方便调用,不需要创建对象,直接使用类名就可以访问。

例子:

public static int chen(int a,int b){
    return a+b;
}

4、怎么用

1、在 java 语言中凡是用 static 修饰的都是类相关的,不需要创建对象,直接通过“类名”即可访问,即 使使用“引用”去访问,在运行的时候也和堆内存当中的对象无关。

2、静态变量也可以使用“引用”去访问,但实际上在 执行过程中,“引用”所指向的对象并没有参与。(但是不建议这样使用)

3、在静态方法中不可以直接调用非静态的方法,必须要通过实例化才可以调用。

4、非静态方法可以直接调用静态方法和非静态方法。

5、什么时候使用

当一个类的所有对象的某个“属性值”不会随着对象的改变而变化的时候,建议将该属性定义为静态属性。

例子:

static int value = 9;
public static void main(String[] args) throws Exception{
    new Test04().printValue();
    value=10;
    System.out.println(value);//输出10
}
public void printValue(){
    int value = 69;
    System.out.println(this.value);//输出9这里使用的是静态变量
}

相反,当一个类的某个属性值会随着对象的改变而改变的时候就要定义为实例属性。

6、静态代码块

格式:

类{

//静态代码块

static{

java 语句;

}

}

1、静态代码块在类加载时执行,并且只执行一次。一般只写一个。

2、静态代码块实际上是 java 语言为程序员准备的一个特殊的时刻,这个时刻就 是类加载时刻,如果你想在类加载的时候执行一段代码,那么这段代码就有的放矢了

3、静态代码块当中的代码 在 main 方法执行之前执行。

当一个方法中不会出现对象的时候参与的时候,那么这个方法一定要定义为“实例方法”。方法中的修饰符就不要加static。

没有static关键字的方法被称为“实例方法”,采用 引用.方法名访问。

没有static关键字的变量被称为实例变量。 采用引用.方法名

继承

一、定义:

1、继承是面向对象的三大特征之一。

2、继承的基本作用就是代码复用,最主要的特征是有了继承才有方法覆盖和多态。

3、继承中继承的是父类中的属性加方法。

二、代码格式:

[修饰符列表] class 类名 extends 父类名{

​ 类体=属性+方法

}

三、注意:

java语言只支持单继承,一个类只能继承一个类。c++支持多继承。但是一个类可以被很多的子类继承。

四、继承中的一些术语:

B类继承A类

A类:父类,基类,超类,superclass。

B类:子类,派生类,subclass。

1、子类继承父类都继承那些数据:

​ 1、构造方法不继承

​ 2、私有的属性不继承

​ 3、其它的都继承

2、java虽然之支持单继承,但是java可以间接继承其他类

​ 例子:

​ A extends B{}

​ B extends c{}

​ c exyends d{}

​ A直接继承B,间接继承串,c,d类。

3、java语言中假设没有继承任何类,默认继承JavaSE中的java.long.object类。

​ java任何一个类都有object类的特征。

方法覆盖:配合多态使用

一、定义;

方法覆盖又被称为方法重写,(override)

二、什么时候会发生方法重写

1、当父类中的方法已经无法满足子类的时候就会发生方法重写

2、重写的方法结构和方法名是一样的即相同的返回值类型,相同的方法名,相同参数列表。

3、方法覆盖之后,子类对象执行的一定是覆盖之后的方法。

三、什么时候满足方法重写

1、方法重写发生在继承的父子类之间

2、建议方法重写的时候复制粘贴

3、访问权限不能更低,可以更高例子:public 和private

4、抛出异常不能更多,可以更少

四、注意:

1、私有方法不能继承,所以不能覆盖。

例子:

public class Test04 {
    private void dosum(){
        System.out.println("1");
    }
    public static void main(String[] args) {
        Test04 tt=new One();
        tt.dosum();
    }
}
class One extends Test04{
    public void dosum(){
        System.out.println("2");
    }
}

2、构造方法不能继承所以不能覆盖。

3、静态方法存在方法覆盖吗?
多态自然就和对象有关系了。
而静态方法的执行不需要对象。
所以,一般情况下,我们会说静态方法“不存在”方法覆盖。
不探讨静态方法的覆盖。

例子:

public class Test04 {
    public static void main(String[] args) {
        c cc=new Bb();//此引用相当于无效
        cc.move();
    }
}
class c{
    public static void move(){
        System.out.println("这是A方法");
    }
}
class Bb extends c{
    public static void move(){
        System.out.println("这是B方法");
    }
}
//输出的是A类中的方法

4、覆盖只针对方法,不谈属性。

5、方法重写的返回值类型:

​ 基本数据类型:必须一致。

​ 引用数据类型:向上可以,向下不可以。

多态

1、java语法允许父类型引用指向子类型对象

2、底层是什么对象,编译器调用的就是谁的方法

封装让类有了独立体的概念

继承让对象之间有了关系

多态让父类型的引用却可以指向子类型的对象,让程序产生了多种类型的概念

第一种为编译期形态,第二种形态为运行期形态

3、什么时候采用向下转型

一、多态的基础语法

1、多态中涉及的概念

​ 1.1向上转型(Upcasting)

​ 子类型转换为父类型,又被称为自动类型转换。

​ 1.2 向下转型 (Downcasting)

​ 指父类型转换为子类型,又被称为强制类型转换。需要使用强制转换符。

注意:无论是向上转型还是向下转型,两种类型之间必须要有继承关系。没有继承关系的不可以进行转换。

二、java的编译阶段和运行阶段

1、Java程序永远都分为编译阶段和运行阶段

2、先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的。

3、

//向上类型转换,自动类型转换。
//父类型引用指向子类型对象
DongWu a1=new Cat();
a1.move();

编译阶段编译器检查a1这个引用的数据类型为DongWu,由于DongWu。class字节码当中有MOVE()方法,所以编译通过了。这个过程称为静态绑定,编译阶段绑定。只有静态绑定成功后才有后续的运行。

4、在程序运行阶段,JVM堆内存中创建的是Cat对象,那么在程序运行的阶段一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,称为运行阶段的绑定。

5、无论Cat类有没有重写MOVE()方法,运行阶段一定调用的是Cat对象的move()方法,因为底层真实对象就是Cat对象

6、父类型引用子类型对象这种机制导致程序在编译阶段和运行阶段绑定两种不同的形态/状态,这种机制可以称为一种多态的语法机制。

7、

Cat a2=(Cat)a1;
a2.move1();

因为在编译阶段编译器检查到a1的数据类型为DongWu类型,从DongWu.class字节码文件中查找move1()方法,最终没有找到该方法,导致静态绑定失败,没有绑定成功,就是编译失败。就没有办法运行,此时就要进行向下转型。

三、异常和异常的处理

3.1、类型转化异常只有在强制类型转换的时候会发生,也就是向下转型的时候存在隐患(编译通过了,但是运行出错了)

例子:

DongWu a3=new Dog();

Cat a4=(Cat) a3;

a4.move1();
//向下转换异常

3.2、异常处理:

使用instanceof运算符可以避免出现以上的错误。

语法格式:

​ 引用 instanceof 数据类型名

以上运算符的结果为布尔类型,结果可能是TRUE/FALSE

例子: a instanceof Cat

TRUE表示a这个引用指向的对象是一个Cat类型

FALSE表示a这个引用指向的对象不是Cat类型

java在规范中要求,在进行强制类型转换的时候,建议采用instanceof运算符进行判断,避免异常的出现,这是一种编程的好习惯。

例子:

DongWu a3 = new Dog();
if (a3 instanceof Cat) {
    Cat a5 = (Cat) a3;
    a5.move1();
} else if (a3 instanceof Dog){
    Dog a6=(Dog) a3;
    a6.move2();
}

四、多态的作用

1、面向对象编程的核心:定义好类,然后将类实例化为对象,给一个环境驱使一下,让各个对象之间协作起来形成一个系统。

2、作用:

​ 降低程序的耦合度,提高程序的拓展力。

​ 能使用多态尽量使用多态。

​ 父类型引用指向子类型对象。

核心:面向抽象编程,尽量不要面向具体编程。

super关键字

一、定义

1、super是一个关键字,全部小写。

2、super关键字代表着当前对象的那一部分父类特征。

3、默认构造方法的第一行有this(),super()。两个不可能同时存在,只存在一个,都没有的时候默认是super()。

4、父类的构造方法一定会执行。

二、super与this对比

​ this:
​ this能出现在实例方法和构造方法中。
​ this的语法是:“this.”、“this()”
​ this不能使用在静态方法中。
​ this. 大部分情况下是可以省略的。
​ this.什么时候不能省略呢? 在区分局部变量和实例变量的时候不能省略。
​ public void setName(String name){
​ this.name = name;
​ }
​ this() 只能出现在构造方法第一行,通过当前的构造方法去调用“本类”中
​ 其它的构造方法,目的是:代码复用。

super:
super能出现在实例方法和构造方法中。
super的语法是:“super.”、“super()”
super不能使用在静态方法中。
super. 大部分情况下是可以省略的。
super.什么时候不能省略呢。当父类中有的属性,子类中也有,在子类中想调用父类中的属性的时候不可以省略。
super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,

​ 目的是:创建子类对象的时候,先初始化父类型特征。

三、能不能使用在静态方法当中

super 不能使用在静态方法当中,因为 super 代表了当前对象上的父类型特征, 静态方法中没有 this,肯定也是不能使用 super 的。

四、super什么时候可以省略

不可以省略的时候:

当父类中有的属性,子类中也有,在子类中想调用父类中的属性的时候不可以省略。

class A {
    int a;
    int b;

    //父类中的有参构造方法
    public A(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

class B extends A {
    int a=3;

    public B() {
        super(1, 2);
    }
    public void move(){
        System.out.println(this.a);
        System.out.println(super.a);
    }

总结:父类和子类中有同名实例变量或者有同名的实例方法,想在子类中访问父类中的实例变量或实例方法,则super 是不能省略的,其它情况都可以省略。

五、super的三种使用

1、super.属性名

2、super.方法名(实参) 调用父类中的方法,当与子类中的方法名相同的时候不可以省略,当不重名的时候可以省略

​ 例子:super.move2(2);

3、super(实参)在构造方法中的使用:这种用法是通过当前的构造方法调用父类的构造方法。

作用:

1、调用父类的构造方法,使用这个构造 方法来给当前子类对象初始化父类型特征。

2、代码复用。

例子:

class A{
    int a ;
    int b;
//父类中的有参构造方法
    public A(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
class B extends A{
    int c;
//子类中的有参构造方法
    public B(int a, int b, int c) {
        super(a, b);
        this.c = c;
    }

super()的作用:初始化当前对象的父类的特征,就是给父类型的参数进行赋值。

super关键字代表着当前对象的那一部分父类特征

super什么时候可以省略

六、注意:

1、 this 是可以单独使用的引用,但 super 无法输出,编译器提示 super 要使用必须是“super.xxx”,显然 super 并不指向独立的对象,并不是保存某个对象的内存地址。

2、super的点一定不可以省略,因为super不可以单独使用。

3、super 不是引用。super也不保存内存地址,super也不指向任何对象。
super 只是代表当前对象内部的那一块父类型的特征。

4、super的jvm图

final关键字

1、定义

1、fina表示最终的,不可改变的。

2、采用 final 修饰的类不能被继承

3、采用 final 修饰的方法不能被覆盖

4、采用 final 修饰的变量不能被修改

例子:

final int a=1;

a=2;错误。

5、 final修饰的变量必须手动初始化,不能采用系统默认值。

​ 局部变量不初始化没有默认值,实例变量不初始化有默认值。

6、如果修饰的引用,那么这个引用只能指向一个对象,也就是说这个引用不能再次赋值, 但被指向的对象是可以修改的。

例子:

 //这是可以的
 A a1=new A();
 a1=new B();
 //这是不可以的
final A a2=new B();
a2=new A();//错误的

a2.b=4;//这是可以的

7、构造方法不能被 final 修饰

会影响 JAVA类的初始化:final 定义的静态常量调用时不会执行 java 的类初始化方法, 也就是说不会执行 static 代码块等相关语句,这是由 java 虚拟机规定的。我们不需要 了解的很深,有个概念就可以了。

**8、final修饰的实例变量一般和static联合使用,称为常量。**最常用
public static final double PI = 3.1415926;

2、final的作用

1、修饰变量 也就是常用的变量

2、修饰方法

个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为final。final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。

3、修饰类

final类通常功能是完整的,它们不能被继承。

类型和类型之间的关系

is a(继承)、has a(关联)、like a(实现)

一、is a:

​ 例子:Cat is a Animal(猫是一个动物)
​ 凡是能够满足is a的表示“继承关系”
​ A extends B

二、has a:

​ 例子: I has a Pen(我有一支笔)
​ 凡是能够满足has a关系的表示“关联关系”
​ 关联关系通常以“属性”的形式存在。
​ A{
​ B b;
​ }

三、like a:

​ 例子:Cooker like a FoodMenu(厨师像一个菜单一样)
​ 凡是能够满足like a关系的表示“实现关系”
​ 实现关系通常是:类实现接口。
​ A implements B

抽象类和接口

一、抽象类

1、抽象类怎么定义?

1、抽象类:类和类之间有共同的特征,将这些具有共同特征的类再进一步抽象成为了抽象类,由于类本身是不存在的,所以抽象类无法创建对象。

抽象类的格式:【修饰符列表】 abstranct class 类名{

}

2、抽象类也属于引用数据类型。

2、抽象类怎么使用

抽象类无法实例化,所以抽象类都是通过子类继承使用的。

3、抽象类的基础知识:

1、抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。

2、final和abstract不能联合使用,这两个关键字是对立的。因为final定义的类无法被继承,但是抽象类就是用来被子类继承的,所以这两个关键字是相互对立的。

3、抽象类的子类可以是抽象类。也可以是非抽象类。

4、抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的。

5、java语言中凡是没有方法体的方法都是抽象方法。错误的。 Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们
都不是抽象方法。

例如:
public native int hashCode();
这个方法底层调用了C++写的动态链接库程序。
前面修饰符列表中没有:abstract。有一个native。表示调用JVM本地程序。

4、抽象方法:

格式:public abstract void doSome();

1、没有方法体。前面修饰符列表中有abstract关键字。

2、抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中。

**3、一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现。**以为一个子类继承父类的方法,如果子类是非抽象类,就一定不能存在抽象方法。

二、接口

1、基础知识

1、接口也是一种“引用数据类型”。编译之后也是一个class字节码文件。

2、接口是完全抽象的。(抽象类是半抽象。)或者也可以说接口是特殊的抽象类。

2、接口怎么定义

格式:【修饰符列表】 interface 接口名{

}

例子:

//定义一个接口A
 interface A{
    int a=1;
}

3、接口怎么调用:

引用.接口名

4、接口里面的内容:

接口中只包含两部分内容:

一、常量。

二、抽象方法。接口中的方法都是抽象方法,所以接口中的方法都没有方法体。

接口中没有其它内容了。只有以上两部分。

注意:

1、接口中所有的元素都是public修饰的。(都是公开的。)

2、接口中的抽象方法定义时:public abstract修饰符可以省略。

3、接口中的常量的public static final可以省略。

4、接口中的重要知识:

1、接口支持多继承,一个接口可以继承多个接口。

格式:【修饰符列表】 interface 接口名 extends 父类接口名1,父类接口名2{

}

2、因为接口就是让其他人实现。所以接口中不可以使用private修饰符。

3、一个非抽象的类,实现接口的时候,必须将接口中所有方法加以实现,,并且一个类可以实现多个接口。。

​ 快速实现接口中的方法:alt+inse选择implements methods即可快速重写接口中的方法。

​ 类实现接口采用关键字implements

例子:**class chen implements A1,B1{**实现A1接口和B1接口

4、extends和implements可以共存,extends在前,implements在后。

例子:class chen1 extends chen implements Fei{

这样写的话继承父类中的方法也必须重写。

5、接口在开发中的作用:

1、注意:接口在开发中的作用,类似于多态在开发中的作用。

​ 多态:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力。
​ 接口在开发中的作用?
​ 接口是不是完全的?是。
​ 而我们以后正好要求,面向抽象编程。
​ 面向抽象编程这句话以后可以修改为:面向接口编程。
​ 有了接口就有了可插拔。可插拔表示扩展力很强。不是焊接死的。

2、接口例子:

主板和内存条之间有插槽,这个插槽就是接口,内存条坏了,可以重新
买一个换下来。这叫做高扩展性。(低耦合度。)

​ 接口在现实世界中是不是到处都是呢?
​ 螺栓和螺母之间有接口
​ 灯泡和灯口之间有接口
​ 笔记本电脑和键盘之间有接口(usb接口,usb接口是不是某个计算机协会制定的协议/规范。)
​ 接口有什么用?扩展性好。可插拔。
​ 接口是一个抽象的概念。

​ 分析:
​ 中午去饭馆吃饭,这个过程中有接口吗?

​ 接口是抽象的。

​ 菜单是一个接口。(菜单上有一个抽象的照片:西红柿炒鸡蛋)

​ 谁面向接口调用。(顾客面向菜单点菜,调用接口。)

​ 谁负责实现这个接口。(后台的厨师负责把西红柿鸡蛋做好!是接口的实现者。)

​ 这个接口有什么用呢?
​ 这个饭馆的“菜单”,让“顾客”和“后厨”解耦合了。
​ 顾客不用找后厨,后厨不用找顾客。他们之间完全依靠这个抽象的菜单沟通。

3、总结

一句话:三个字“解耦合”

​ 面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。
​ 接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)

​ 接口可以解耦合,解开的是谁和谁的耦合!!!
​ 任何一个接口都有调用者和实现者。
​ 接口可以将调用者和实现者解耦合。
​ 调用者面向接口调用。
​ 实现者面向接口编写实现。

​ 以后进行大项目的开发,一般都是将项目分离成一个模块一个模块的,
​ 模块和模块之间采用接口衔接。降低耦合度。

三、抽象类和接口有什么区别?

在这里我们只说一下抽象类和接口在语法上的区别。
至于以后抽象类和接口应该怎么进行选择,通过后面的项目去体会/学习。

​ 1、抽象类是半抽象的。
​ 接口是完全抽象的。

​ 2、抽象类中有构造方法。
​ 接口中没有构造方法。

3、接口和接口之间支持多继承。
类和类之间只能单继承。

​ 4、一个类可以同时实现多个接口。
​ 一个抽象类只能继承一个类(单继承)。

​ 5、接口中只允许出现常量和抽象方法。

​ 6、这里先透露一个信息:
​ 以后接口使用的比抽象类多。一般抽象类使用的还是少。
​ 接口一般都是对“行为”的抽象。

包机制和import机制

一、为什么要使用package?

package是java中包机制。包机制的作用是为了方便程序的管理。
不同功能的类分别存放在不同的包下。(按照功能划分的,不同的软件包具有不同的功能。)

简单来说就是方便程序的管理。

二、package怎么用?

package是一个关键字,后面加包名。例如:
package com.bjpowernode.javase.chapter17;
注意:package语句只允许出现在java源代码的第一行。

补充:以后说类名的时候,如果带着包名描述,表示完整类名。
如果没有带包,描述的话,表示简类名。
java.util.Scanner 完整类名。
Scanner 简类名

三、命名规范

一般都采用公司域名倒序的方式(因为公司域名具有全球唯一性。)
包名命名规范:
公司域名倒序 + 项目名 + 模块名 + 功能名

四、有package的java程序怎么编译?怎么运行?(了解即可,以后用集成开发环境,很少用到这个)

采用之前的编译和运行不行了。
类名不再是:HelloWorld了。
类名是:com.bjpowernode.javase.chapter17.HelloWorld

​ 编译:
​ javac -d . HelloWorld.java
​ 解释一下:
​ javac 负责编译的命令
​ -d 带包编译
​ . 代表编译之后生成的东西放到当前目录下(点代表当前目录)
​ HelloWorld.java 被编译的java文件名。

​ 运行:
​ java com.bjpowernode.javase.chapter17.HelloWorld

五、关于import的使用。

5.1、import什么时候使用?

​ A类中使用B类。
​ A和B类都在同一个包下。不需要import。
​ A和B类不在同一个包下。需要使用import。
​ java.lang.*;这个包下的类不需要使用import导入。

5.2、import怎么用?

​ 1、import 完整类名;
​ 2、import 包名.*;

​ 3、import语句只能出现在package语句之下,class声明语句之上。

5.3、注意:

​ 1、import java.util.Scanner; // 完整类名。

​ // 同学的疑问:这样是不是效率比较低。
​ // 这个效率不低,因为编译器在编译的时候,会自动把变成具体的类名。
​ import java.util.
;

​ // 想省懒劲你不能太省了。
​ 2、import java.; 这是不允许的,因为在java语言中规定,这里的只代表某些类的名字。

​ 3、import *是不可以的。import java.*也是不可以的,*只代表类名。

object类中的方法

一、object类

这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。

任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。

1.1、Object类当中有哪些常用的方法?

​ 我们去哪里找这些方法呢?
​ 第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
​ 第二种方法:去查阅java的类库的帮助文档。

1.2、什么是API?

​ 应用程序编程接口。(Application Program Interface)
​ 整个JDK的类库就是一个javase的API。
​ 每一个API都会配置一套API帮助文档。
​ SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)

1.3、目前为止我们只需要知道这几个方法即可:

​ protected Object clone() // 负责对象克隆的。
​ int hashCode() // 获取对象哈希值的一个方法。
​ boolean equals(Object obj) // 判断两个对象是否相等
​ String toString() // 将对象转换成字符串形式
​ protected void finalize() // 垃圾回收器负责调用的方法

二、toString()

2.1、源代码长什么样?

​ public String toString() {
​ return this.getClass().getName() + “@” + Integer.toHexString(hashCode());
​ }
​ 源代码上toString()方法的默认实现是:
​ 类名@对象的内存地址转换为十六进制的形式

2.2、设计toString()方法的目的是什么?

​ toString()方法的作用是什么?
​ toString()方法的设计目的是:通过调用这个方法可以将一个“java对象”转换成“字符串表示形式”

2.3、建议所有的子类都去重写toString()方法。

其实SUN公司开发java语言的时候,建议所有的子类都去重写toString()方法。

toString()方法应该是一个简洁的、详实的、易阅读的.

2.4、注意

1、以后所有类的toString()方法是需要重写的。重写规则,越简单越明了就好。

2、System.out.println(引用); 例子:System.out.println(rq1.toString());

这里会自动调用“引用”的toString()方法。

3、String类是SUN写的,toString方法已经重写了。

三、equals()方法

3.1、equals方法的源代码

​ public boolean equals(Object obj) {
​ return (this == obj);
​ }
​ 以上这个方法是Object类的默认实现。

3.2、设计equals方法的目的是什么?

​ 1、以后编程的过程当中,都要通过equals方法来判断两个对象是否相等。
​ 2、功能:equals方法是判断两个对象是否相等的。

3.3、默认的equals方法够不够用

1、在Object类中的equals方法当中,默认采用的是“”判断两个java对象是否相等。而“”判断的是两个java对象的内存地址,我们应该判断两个java对象的内容是否相等。所以老祖宗的equals方法不够用,需要子类重写equals。

3.4、判断两个java对象是否相等

1、判断两个java对象是否相等,不能使用“”,因为“”比较的是两个对象的内存地址。

3.5、使用场景

1、基本数据类型比较实用:==
2、对象和对象比较:调用equals方法

3.6、注意:

1、以后所有类的equals方法也需要重写,因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。

2、重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。

3、String类是SUN编写的,所以String类的equals方法重写了。以后判断两个字符串是否相等,最好不要使用==,要调用字符串对象的equals方法。因为String有两种形式:你不知道有当时定义的时候用的是哪种形式。所以比较的时候最好都用equals方法。

​ 1、string =“abc”;

​ 2、string s2=new string(“abc”);

4、重写equals方法的时候要彻底。

5、此方法用IDEA可以自动生成。

四、String类

String类重写了toString()和equals()方法。

//String中重写了toString()和equals()
public class Test02 {
    public static void main(String[] args) {
        String s1="abc";
        String s2=new String("abc");
        String s3="abc";
        System.out.println(s1==s2);//false
        System.out.println(s1==s3);//true
        
        //String中重写了equals()方法
        System.out.println(s1.equals(s2));//true
        
        //string中重写了toString()方法
        String x=new String("陈绪杰");
        
        //可以直接输出引用
        System.out.println(x);//陈绪杰
        System.out.println(x.toString());//陈绪杰
    }
}

五、finalize()方法:JDK9以上的版本就不用了

4.1、finalize()方法。垃圾销毁时机

​ 这个方法是protected修饰的,在Object类中这个方法的源代码是?

​ protected void finalize() throws Throwable { }

​ finalize()方法只有一个方法体,里面没有代码,

​ GC:负责调用finalize()方法。

4.2、finalize()方法的执行时机:

1、当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法。

2、finalize()方法实际上是SUN公司为java程序员准备的一个时机,垃圾销毁时机
如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法当中。

4.3、怎么使用:

当一个对象为空的时候可能会被调用例子:

A a=new A();
a=null;

1、这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法。
2、finalize()只需要重写,重写完将来自动会有程序来调用。

4.4、提示:

1、java中的垃圾回收器不是轻易启动的。

2、垃圾太少,或者时间没到,种种条件下。

3、有可能启动,也有可能不启动。

4、 有一段代码可以建议垃圾回收器启动。

			System.gc(); // 建议启动垃圾回收器。(只是建议,可能不启动,也可能启动。启动的概率高了一些。)jdk8启动的概率高。

4.5、应用场景

项目开发中有这样的业务需求:所有对象在JVM中被释放的时候,请记录一下释放时间!!!

六、hashCode()

6.1、hashCode方法?返回一个对象的哈希码。

​ public native int hashCode();

​ 这个方法不是抽象方法,带有native关键字,底层调用C++程序。

6.2、hashCode()方法返回结果:

1、hashCode()方法返回的是哈希码:

2、实际上就是一个java对象的内存地址,经过哈希算法,得出的一个值。所以hashCode()方法的执行结果可以等同看做一个java对象的内存地址。

6.3、使用方法:

直接调用,不需要重写

例子:

A a=new A();

System.out.println(a.hashCode());

内部类

一、定义:

在一个类的内部定义的类,称为内部类。

二、实例内部类

1、创建:public class A{}

1、创建实例内部类,外部类的实例必须已经创建

2、实例内部类会持有外部类的引用

3、实例内部不能定义 static 成员,只能定义实例成

4、内部类可以使用 private 和 protected 修饰

5、使用:外部类.内部类 引用=new 外部类().内部类();l 例子:InnerClassTest01.Inner1 inner1 = new InnerClassTest01(100, 200).new Inner1();

三、局部内部类

1、局部内部类是在方法中定义的,它只能在当前方法中使用。

2、局部变量的作用一样 局部内部类和实例内部类一致,不能包含静态成员

3、局部变量,在内部类中使用必须采用 final 修饰

四、静态内部类

1、静态内部类不会持有外部的类的引用,创建时可以不用创建外部类

2、静态内部类可以访问外部的静态变量,如果访问外部类的成员变量必须通过外部类的实例访问

3、创建: static class A{}

五、匿名内部类(使用最多)

是一种特殊的内部类,该类没有名字

例子:记住这种语法就可以了。

b.move1(new A(){
            public int sum(int a, int b){
                return a+b;
            }
        },100,200);


    }

}
interface A{
    public int sum(int a, int b);
}

二维数组

一、定义

二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。

二、二维数组的静态初始化和动态初始化

2、1静态初始化:

	int[][] arr = {
								{1,2,34},
								{54,4,34,3},
								{2,34,4,5}
							};

		Object[][] arr = {
								{new Object(),new Object()},
								{new Object(),new Object()},
								{new Object(),new Object(),new Object()}
							};

2、2动态初始化:

	int[][] arr = new int[3][4];
		Object[][] arr = new Object[4][4];
		Animal[][] arr = new Animal[3][4];
		// Person类型数组,里面可以存储Person类型对象,以及Person类型的子类型都可以。
		Person[][] arr = new Person[2][2];
		....

三、二维数组的遍历

for(int i = 0; i < arr.length; i++){ // 外层for循环负责遍历外面的一维数组。
			// 里面这个for循环负责遍历二维数组里面的一维数组。
			for(int j = 0; j < arr[i].length; j++){
				System.out.print(arr[i][j]);
			}
			// 换行。
			System.out.println();
		}

数组

一、定义:

1、数组是一组数据的集合 ,存放在堆内存之中。

2、数组作为一种引用类型

3、数组元素的类型可以是基本类型,也可以是引用类型,但同一个数组只能是同一种类型

4、数组作为对象,数组中的元素作为对象的属性,除此之外数组还包括一个成员属性 length, length 表示数组的长度

5、数组的长度在数组对象创建后就确定了,就无法再修改了

6、数组元素是有下标的,下标从 0 开始,也就是第一个元素的下标为 0,依次类推最后一个 堆内存 length:n 元素 0 元素 1 … … 元素 n-1,元素的下标为 n-1,我们可以通过数组的下标来访问数组的元素。

7、数组的分类:一维数组、二维数组、三维数组、多维数组…(一维数组较多,二维数组偶尔使用!)

8、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规则的挨着排列的)是连续的。内存地址连续。这是数组存储元素的特点(特色)。数组实际上是一种简单的数据结构。

9、所有的数组都是拿“第一个小方框的内存地址”作为整个数组对象的内存地址。

二、优缺点(面试经常考)

1、优点:

1、查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
为什么检索效率高?
第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以
通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位
元素,所以数组的检索效率是最高的。

​ 数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的,
​ 因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个
​ 内存地址,直接定位的。)

2、缺点:

1、由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
2、第二:数组不能存储大数据量,为什么?
因为很难在内存空间上找到一块特别大的连续的内存空间。

注意:对于数组中最后一个元素的增删,是没有效率影响的。

三、数组的声明与创建:

1、声明:

格式:数据类型[] 数组名; 例子int[] arrary;

格式二:数据类型 数组名[];这是c++的格式,java一般不用。

2、创建:

基本数据类型: 例子:int[] a1=new a1[3];

引用数据类型: 例子:String[] a2=new a2[4];

四、数组的初始化与使用:

1、静态初始化:

例子:

1、基本数据类型:int[] arrary={1,2,3};

2、引用数据类型:object[] a2={new A(),new B(),new C()};

2、动态初始化:

例子:

1、基本数据类型:int[] arrary=new int[2];默认为0

2、引用数据类型:object[] a2=new object[4] ;默认为null

3、什么时候静态初始化,什么时候动态初始化

1、当知道数据的时候使用静态初始化。

2、不知道数据的时候使用动态初始化。

4、使用:

1、格式:数组名[下标];

2、下标不能越界,否则会出现异常错误。

5、遍历

一维数组的遍历
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}

五、数组作为方法的参数传递的时候

参数的几种方式:

例子:

//创建两个数组
//静态初始化基本数据类型
int[] a1={1,2,3,4};

1、move(a1);

2、move2(new int[]{1,2,3,4});//记住这个如果直接传递一个静态数组的话,语法必须这样写。

//动态初始化基本数据类型
3、move2(new int[2]);

//静态初始化引用数据类型
String[] a3=new String[4];
4、move3(a3);

六、main函数中的string[] arg数组

1、非重点,了解一下,以后一般都是有界面的,用户可以在界面上输入用户名和密码等参数信息。

main方法上面的“String[] args”有什么用?
分析以下:谁负责调用main方法(JVM)
JVM调用main方法的时候,会自动传一个String数组过来。

​ 这个方法程序员负责写出来,JVM负责调用。JVM调用的时候一定会传一个String数组过来。

2、主要是接收用户输入参数的。这个参数自动会被转换为“String[] args”。

七、引用数据类型的数组深研究

1、对于数组来说,实际上只能存储java对象的“内存地址”。数组中存储的每个元素是“引用”。

2、引用数据类型的数组,里面只能存放一种类型的对象,但是可以存放该类子类创建的对象。

例子:

//DongWu[] dongWus={new DongWu(),new Dog()}错误的写法,数组的数据类型要一样
DongWu[] dongWus={new DongWu(),new Dog(),new Cat()};//继承过以后就可以使用

3、如果要调用子类特有的方法就要向下转型,如果不是就不需要。

八、数组扩容

1、基础知识:

1、数组的拷贝:System.arraycopy()方法的使用
2、数组有一个特点:长度一旦确定,不可变。

3、所以数组长度不够的时候,需要扩容,扩容的机制是:新建一个大数组,将小数组中的数据拷贝到大数组,然后小数组对象被垃圾回收。

2、System.arraycopy()方法的使用:

1、介绍:System.arraycopy(小数组,小数组的起始位置,大数组,大数组的起始位置,拷贝长度)

3、结论

1、数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。

2、可以在创建数组对象的时候预估计以下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。

数组工具类

一、Arrays.sort排序算法

int[] a1={3,5,2,1,9,6};
//对数组进行排序
Arrays.sort(a1);
for (int i = 0; i < a1.length ; i++) {
    System.out.println(a1[i]);//123569
}

二、Arrays.binarySearch二分查找

1、要先排序过后才能查找

//查找数组中的元素
int a2=Arrays.binarySearch(a1,5);
System.out.println("要查找元素的下标为:"+a2);

三、注意

1、工具类中的方法一般都是静态的。

2、数组工具类在java.until.Arrary中。

常用类-String

一、string

1、String表示字符串类型,属于引用数据类型,不属于基本数据类型。

2、因为字符串在实际的开发中使用太频繁。为了执行效率,所以把字符串放到了方法区的字符串常量池当中。

3、在java中随便使用双引号括起来的都是String对象。例如:“abc”,“def”,“hello world!”,这是3个String对象。

4、java中规定,双引号括起来的字符串,是不可变的,也就是说"abc"自出生到最终死亡,不可变,不能变成"abcd",也不能变成"ab"

5、字符串的比较必须使用equals方法。

6、String已经重写了toString()和equals()方法。

7、堆区中是运行期分配的,常量池中是编译器分配的。

8、只要采用双引号赋值字符串,那么在编译期将会放到方法区中的字符串的常量池里,如果是运行时对字符串相加或相减会放到堆中(放之前会先验证方法区中是否含有相同的字符 串常量,如果存在,把地址返回,如果不存在,先将字符串常量放到池中,然后再返回该对 象的地址)

二、string的构造方法:查看API文档

​ String s = “abc”; 最常用
​ String s = new String(“abc”);
​ String s = new String(byte数组); byte[] b1={97,98,99}; String s3=new String(b1);
​ String s = new String(byte数组, 起始下标, 长度); String s4=new String(b1,0,2);
​ String s = new String(char数组); char[] c1={‘中’,‘国’,‘人’}; String s5=new String(c1);
​ String s = new String(char数组, 起始下标, 长度); String s6=new String(c1,1,2);

三、内存图

String s1="abc";
String s4=new String("chen");
System.out.println(s4);//自动调用已经重写以后的tostring()方法
String s1="chen";
String s2="chen";
System.out.println(s1==s2);//true
String s3=new String("chen");
String s4=new String("chen");
System.out.println(s1==s3);//false
System.out.println(s3==s4);//false
System.out.println(s3.equals(s4));//true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i2pHkLXv-1645954130480)(D:\BaiduNetdiskDownload\JAVA\09-JavaSE进阶每章课堂画图\03-常用类\001-String的内存图.png)]

四、常用方法

1、(掌握).char charAt(int index) 作用:返回下标的那个字符

2、(掌握).int indexOf(String str) 作用:判断某个子字符串在当前字符串中第一次出现处的索引(下标)。

3、(掌握).int lastIndexOf(String str) 作用:判断某个子字符串在当前字符串中最后一次出现的索引(下标)

4、(了解).int compareTo(String anotherString) 作用:比较字符串的大小

5、(掌握).boolean equals(Object anObject) 作用:判断两个字符串是否相等
比较两个字符串必须使用equals方法,不能使用“==”
equals方法有没有调用compareTo方法? 老版本可以看一下。JDK13中并没有调用compareTo()方法。
equals只能看出相等不相等。
compareTo方法可以看出是否相等,并且同时还可以看出谁大谁小。

6、(掌握).boolean equalsIgnoreCase(String anotherString) 作用:判断两个字符串是否相等,并且同时忽略大小写。

7、(掌握).boolean contains(CharSequence s) 作用:判断前面的字符串中是否包含后面的子字符串。

8、(掌握). boolean endsWith(String suffix) 作用:判断当前字符串是否以某个子字符串结尾。

9、(掌握)、boolean startsWith(String prefix) 作用:判断某个字符串是否以某个子字符串开始。

10、(掌握).byte[] getBytes() 作用:将字符串对象转换成字节数组。

11、(掌握).boolean isEmpty() 作用:判断某个字符串是否为“空字符串”。底层源代码调用的应该是字符串的length()方法。

12、(掌握). int length() 作用:判断字符串长度。

13、(掌握). String replace(CharSequence target, CharSequence replacement) 作用:将什么替换成什么

14、(掌握).String[] split(String regex) 作用:拆分字符串。
15、掌握)、 String substring(int beginIndex) 参数是起始下标。作用:从哪个位置开始截取字符串

16、(掌握)、String substring(int beginIndex, int endIndex)作用:从哪个位置开始到哪个位置结束(不包括那个位置)截取字符串。

17、(掌握)、char[] toCharArray() 作用: 将字符串转换成char数组。

18、(掌握)、String toLowerCase() 作用:将字符串全部转化为小写的。

19、(掌握)、String toUpperCase(); 作用:将字符串全部转化为大写的。

20、(掌握). String trim(); 作用:去除字符串前后空白。

21、(掌握). String中只有一个方法是静态的,static String valueOf(数据类型 变量)不需要new对象 这个方法叫做valueOf 作用:将“非字符串”转换成“字符串”。

22、通过源代码可以看出:为什么输出一个引用的时候,会调用toString()方法!!!。
本质上System.out.println()这个方法在输出任何数据的时候都是先转换成字符串,再输出。

五、面试题:

1、面试题:s1=new string(“abc”) s2=new string(“abc”)。这是三个对象,方法区的常量池中有一个,堆内存中有两个对象。

使用 String 时,不建议使用 new 关键字,因为使用 new 会创建两个对象。

2、面试题:判断数组长度和判断字符串长度不一样。判断数组长度是length属性,判断字符串长度是length()方法。

stringBuffer

一、定义:

如果以后需要进行大量字符串的拼接操作,建议使用JDK中自带的:

  • java.lang.StringBuffer

  • java.lang.StringBuilder

  • 使用""+“这样会占用大量的方法区内存。造成内存空间的浪费。

    StringBuffer底层实际上是一个byte[]数组

    往StringBuffer中存放字符串,实际上就是放到byte[]数组中。

    StringBuffer的初始化容量是16.

二、使用

1、拼接字符串,以后拼接字符串统一调用 append()方法。append是追加的意思。

例子:

StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append(10);

2、append方法底层在进行追加的时候,如果byte数组满了,会自动扩容。

三、优化

1、在创建StringBuffer的时候尽可能给定一个初始化容量。

例子:StringBuffer sb=new StringBuffer(100);

2、最好减少底层数组的扩容次数。预估计一下,给一个大一些初始化容量。

3、关键点:给一个合适的初始化容量。可以提高程序的执行效率。

四、面试:

1、说出如何优化。

2、创建一个初始化容量为16个byte[] 数组。(字符串缓冲区对象)。创建对象的时候自动创建一个初始化容量为16byte【】的数组。

3、

1、面试题:String为什么是不可变的?
我看过源代码,String类中有一个byte[]数组,这个byte[]数组采用了final修饰,
因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不
可再指向其它对象,所以String是不可变的!
“abc” 无法变成 “abcd”

2、StringBuilder/StringBuffer为什么是可变的呢?
我看过源代码,StringBuffer/StringBuilder内部实际上是一个byte[]数组,
这个byte[]数组没有被final修饰,StringBuffer/StringBuilder的初始化
容量我记得应该是16,当存满之后会进行扩容,底层调用了数组拷贝的方法
System.arraycopy()…是这样扩容的。所以StringBuilder/StringBuffer
适合于使用字符串的频繁拼接操作。

五、StringBuffer和StringBuilder的区别?

StringBuffer中的方法都有:synchronized关键字修饰。表示StringBuffer在多线程环境下运行是安全的。

StringBuilder中的方法都没有:synchronized关键字修饰,表示StringBuilder在多线程环境下运行是不安全的。

StringBuffer是线程安全的。

StringBuilder是非线程安全的。

八种基本数据类型对应的包装类

一、包装类存在有什么用?

1、方便编程。

2、Java.lang下

二、八种包装类的类名是什么?

1、Byte java.lang.Byte(父类Number)
2、Short java.lang.Short(父类Number)
3、Integer java.lang.Integer(父类Number)
4、Long java.lang.Long(父类Number)
5、Float java.lang.Float(父类Number)
6、Double java.lang.Double(父类Number)
7、Boolean java.lang.Boolean(父类Object)
8、Character java.lang.Character(父类Object)

三、装箱与拆箱

1、装箱:基本数据类型 -(转换为)->引用数据类型(装箱)

例子:

//第一种
Integer i1=new Integer(101);
System.out.println(i1.toString());
//第二种
int i2=Integer.valueOf(100);
System.out.println(i2);

2、拆箱:将引用数据类型–(转换为)-> 基本数据类型(拆箱)

int i3=i1.intValue();
System.out.println(i3);

3、以后常用的是自动拆箱与自动装箱

​ Integer i1=100;
​ int i3=i1;

1、自动装箱:基本数据类型自动转换成包装类。

2、自动拆箱:包装类自动转换成基本数据类型。

3、+,-,*,/会自动触发拆箱与装箱,==不会触发。

4、java中为了提高程序的执行效率,将[-128到127]之间所有的包装对象提前创建好,放到了一个方法区的“整数型常量池”当中了,目的是只要用这个区间的数据不需要再new了,直接从整数型常量池当中取出来。

Integer i4=100;
Integer i5=100;
System.out.println(i4==i5);//true在常量池中的地址是一样的
Integer i6=200;
Integer i7=200;
System.out.println(i6==i7);//false常量池中不存在,所以地址不一样
System.out.println(i4.equals(i5));//true

四、Integer

Integer a=100;
Integer b=100;
System.out.println(a==b);
常量池中只保存-127~127之间的常量,超过则不保存,他们的地址就不一样了。

1、构造方法

Integer(int)

Integer i1=new Integer(100);

Integer(String)

Double i3=new Double(3.14);

2、通过访问包装类的常量,来获取最大值和最小值

System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);

3、parseInt

​ Integer.parseInt(“123”)
​ Integer.parseInt(“中文”) : NumberFormatException

4、Integer.valueOf()

五、String int Integer之间互相转换

//1、string-》int
int i1=Integer.parseInt("123");
System.out.println(i1);

//2、int->string
String i2=123+"";
System.out.println(i2);

//3、int->Integet
Integer i3=100;
System.out.println(i3);

//4、Integet-》int
int i4=i3;
System.out.println(i4);

//5、string-》Integet
int i5=Integer.valueOf("12345");
System.out.println(i5);

//6、Integet->string
String i6=String.valueOf(100);
System.out.println(i6);

日期类java.util.Date

一、获取系统当前时间

​ Date d = new Date();

二、日期格式化:java.text.SimpleDateFormat

1、

yyyy 年(年是4位)
MM 月(月是2位)
dd 日
HH 时
mm 分
ss 秒
SSS 毫秒(毫秒3位,最高999。1000毫秒代表1秒)
注意:在日期格式中,除了y M d H m s S这些字符不能随便写之外,剩下的符号格式自己随意组织。

2、Date --> String
SimpleDateFormat sdf = new SimpleDate(“yyyy-MM-dd HH:mm:ss SSS”);
String s = sdf.format(new Date());

3、String --> Date

​ SimpleDateFormat sdf = new SimpleDate(“yyyy-MM-dd HH:mm:ss”);
​ Date d = sdf.parse(“2008-08-08 08:08:08”);
4、格式化日期:获取当前时间的步骤:

SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);

String s = sdf.format(new Date());

System.out.println(s);

注意:字符串的日期格式和SimpleDateFormat对象指定的日期格式要一致。不然会出现异常:java.text.ParseException

四、获取毫秒数

作用:经常用来计算一个方法的执行时间

​ long begin = System.currentTimeMillis();

五、System

System.out 【out是System类的静态变量。】
System.out.println() 【println()方法不是System类的,是PrintStream类的方法。】
System.gc() 建议启动垃圾回收器
System.currentTimeMillis() 获取自1970年1月1日到系统当前时间的总毫秒数。
System.exit(0) 退出JVM。

数字格式化

一、DecimalFormat数字格式化java.txt.DecimalFormat

​ ###,###.## 表示加入千分位,保留两个小数。
​ ###,###.0000 表示加入千分位,保留4个小数,不够补0

二、BigDecimal java.math.BigDecimal

1、 BigDecimal 属于大数据,精度极高。不属于基本数据类型,属于java对象(引用数据类型)这是SUN提供的一个类。专门用在财务软件当中。

2、BigDecimal i5=new BigDecimal(100);

随机数与枚举

一、随机数

1、怎么产生int类型随机数。

​ Random r = new Random();
​ int i = r.nextInt();

2、怎么产生某个范围之内的int类型随机数。

​ Random r = new Random();
​ int i = r.nextInt(101); // 产生[0-100]的随机数。

二、枚举

1、定义:

1、枚举是一种引用数据类型。

2、枚举编译之后也是class文件。
3、枚举类型怎么定义?
enum 枚举类型名{
枚举值,枚举值2,枚举值3
}

2、使用地方:

1、当一个方法执行结果超过两种情况,并且是一枚一枚可以列举出来的时候,建议返回值类型设计为枚举类型。

异常

一、定义:

java中异常的作用是:增强程序健壮性。

java中异常以类和对象的形式存在。发生异常的时候JVM会自动创建异常对象。故所以所有的异常都是发生在程序运行阶段的。

二、结构

Object
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:

​ **Exception的直接子类
RuntimeException

三、异常的分类

1、编译时异常:在编写程序的时候预先对异常进行处理,如果不处理编译器会报错。

​ 发生的概率比较高。

​ 也叫受控异常、受检异常。

2、运行时异常:在编写程序的阶段可以选择处理,也可以选择不处理。

​ 发生的概率比较低。

​ 也叫非受控异常、未受检异常。

四、异常的处理方式

怎么选择:如果想让调用者去处理,则使用第一种方法,其他选择第二种方式

1、在方法声明的位置上,使用throws关键字,抛给上一级。(就是抛给调用者来处理)

​ throws后面可以跟多个异常,中间用,隔开。

​ throws可以上抛给更高级的异常类。

​ 如果发现异常,后面的语句不再执行。

2、使用try…catch语句进行异常的捕捉。

		语法:try {
        // try尝试
        m1();
        // 以上代码出现异常,直接进入catch语句块中执行。
        System.out.println("hello world!");
    } catch (FileNotFoundException e){ // catch后面的好像一个方法的形参。
        // 这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址。
        // catch是捕捉异常之后走的分支。
        // 在catch分支中干什么?处理异常。
        System.out.println("文件不存在,可能路径错误,也可能该文件被删除了!");
        System.out.println(e); //java.io.FileNotFoundException: D:\course\01-课\学习方法.txt (系统找不到指定的路径。)
    }
    // try..catch把异常抓住之后,这里的代码会继续执行。
    System.out.println("main over");

注意:
1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
2、catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
3、catch写多个的时候,从上到下,必须遵守从小到大。
4、JDK8的新特性:catch(FileNotFoundException | ArithmeticException | NullPointerException e)

注意:Java中异常发生之后如果一直上抛,最终抛给了main方法

main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行。

采用第一种方法处理异常,发生异常以后后面的代码不会执行。

采用第二种方法处理 异常,try中异常后面的代码不会执行,但是catch后的语句会继续执行。

五、异常对象两个重要的方法

1、 获取异常简单的描述信息:
String msg = exception.getMessage();

2、 打印异常追踪的堆栈信息:一般在写程序的时候使用这个

​ java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。

​ exception.printStackTrace();

3、我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢?
异常信息追踪信息,从上往下一行一行看。
但是需要注意的是:SUN写的代码就不用看了(看包名就知道是自己的还是SUN的。)。
主要的问题是出现在自己编写的代码上。

六、finally

1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。

finally子句必须和try一起出现,不能单独编写。

2、finally语句通常使用在哪些情况下呢?

通常在finally语句块中完成资源的释放/关闭。

​ 因为finally中的代码比较有保障。

​ 即使try语句块中的代码出现异常,finally中代码也会正常执行。

3、ry和finally,没有catch可以吗?可以。

​ try不能单独使用。

​ try finally可以联合使用。

​ 以下代码的执行顺序:
​ 先执行try…
​ 再执行finally…
​ 最后执行 return (return语句只要执行方法必然结束。)

4、 退出JVM
System.exit(0);

退出JVM之后,finally语句中的代码就不执行了!

七、自定义异常

第一步:编写一个类继承Exception或者RuntimeException.

第二步:提供两个构造方法,一个无参数的,一个带有String参数的。

例子:

public class MyException extends Exception{ // 编译时异常
    public MyException(){

    }
    public MyException(String s){
        super(s);
    }
}

八、异常在开发中的作用

当判断错误的时候可以使用

步骤:

1、自己定义一个异常

2、throw new 自己定义的异常,采用上抛的方式处理异常

3、在测试类中采用捕捉的方式处理异常,输出e.getmessage()

集合

一、集合的作用:

1、集合就是一个容器,可以存储多个元素。

2、集合在java.util.*包下

二、集合存储的是什么

集合不能存储基本数据类型,也不能存储JAVA对象

存储的是对象的内存地址,任何时候集合存储的都是引用。

三、集合的分类

1、单个方式存储

主要的:

List:

有序可重复,有序指的是存进去的顺序与取出来的顺序是一样的,元素有下标。下标从0开始

特有的方法:

void add(int index, Object element):在指定位置添加元素

Object set(int index, Object element):修改指定位置的元素

Object get(int index):获得指定位置的元素

int indexOf(Object o):返回元素第一次出现的下标

int lastIndexOf(Object o):返回元素最后一次出现的下标

Object remove(int index):删除指定位置的元素

set:

无序不可重复。元素没有下标。

2、键值对的方式存储

1、定义:

1、Map:无序不可重复的

2、Map和Collection没有继承关系。

3、Map集合以key和value的方式存储数据:键值对

4、key和value都是引用数据类型。

5、key和value都是存储对象的内存地址。

6、key起到主导的地位,value是key的一个附属品。

2、Map接口中常用方法:

1、V put(K key, V value) 向Map集合中添加键值对

2、V get(Object key) 通过key获取value

3、void clear() 清空Map集合

4、boolean containsKey(Object key) 判断Map中是否包含某个key

5、boolean containsValue(Object value) 判断Map中是否包含某个value

6、boolean isEmpty() 判断Map集合中元素个数是否为0

7、V remove(Object key) 通过key删除键值对

8、int size() 获取Map集合中键值对的个数。

9、 Collection values() 获取Map集合中所有的value,返回一个Collection

10、 Set keySet() 获取Map集合所有的key(所有的键是一个set集合)

3、map集合的遍历:重要

第一种都是先获得key的值,然后再用map.get(key)方法获得value的值

1、采用迭代器的方法遍历

//把所有的key存放在set集合之中
Set<Integer> set=map.keySet();
//遍历
Iterator<Integer> it=set.iterator();
while (it.hasNext()){
    Integer k1=it.next();
    System.out.println("键"+k1+"值:"+map.get(k1));
}

2、使用foreach

for(Integer k3:set){
    System.out.println(k3);
    System.out.println(map.get(k3));
}

第二种方式,一起获得key的值和value的值

Set<Map.Entry<Integer,String>> set2=map.entrySet();
for(Map.Entry<Integer,String> map2:set2){
    Integer k4=map2.getKey();
    String value=map2.getValue();
    System.out.print("键:"+k4+"值:"+value);
    System.out.println();
}

1、ArrayList:把检索发挥到极致。

定义:在数据读取的时候由于通过数组的索引直接访问,因此查询有着优异的性能,但是在进行新增数据的时候数据达到长度的临界值的时候将会收到影响,或者在对数据进行删除的时候性能也会受到很大的影响

1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)

2、集合底层是一个Object[]数组。ArrayList集合扩容是原容量1.5倍。

3、构造方法:
new ArrayList();
new ArrayList(20);
4、ArrayList集合底层是数组,怎么优化?
尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集合 的时候预估计元素的个数,给定一个初始化容量。
8、面试官经常问的一个问题?
这么多的集合中,你用哪个集合最多?
答:ArrayList集合。
因为往数组末尾添加元素,效率不受影响。
另外,我们检索/查找某个元素的操作比较多。

7、ArrayList集合是非线程安全的。(不是线程安全的集合。)

List myList = new ArrayList(); // 非线程安全的。

Collections.synchronizedList(myList); // 变成线程安全的************

8、遍历 第一种:通过索引遍历for(i=0;i<list.size();i++)

​ 第二种:通过迭代器for(Object 0:list){System.out.println(o);}

9、获取集合中元素的索引位置 通过值进行索引 list.indexof(值)。元素不存在则返回-1

10、ArrayList之所以检索效率比较高,不是单纯因为下标的原因。是因为底层数组发挥的作用。

2、LinkList:把随机增删发挥到极致。

底层:双向链表,在空间存储上,内存地址不连续。

作用:用来插入和删除,当频繁的进行查找和删除的时候就用这个集合

优缺点:检索效率比较低,需要从头便利,增删效率比较高

LinkedList集合有初始化容量吗?没有,最初这个链表中没有任何元素。first和last引用都是null。

通常用pojo描述一个简单的额类

3、vector:

底层也是一个数组,初始化容量:10。

怎么扩容的?扩容之后是原容量的2倍。10–> 20 --> 40 --> 80

Vector中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的。效率比较低,使用较少了。

怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
使用集合工具类: java.util.Collections;

例子: List myList = new ArrayList(); // 非线程安全的。

	**Collections.synchronizedList(myList);    // 变成线程安全的**

4、HashSet

底层是HashMap,放到HashSet,集合中的元素等同于放到HashMap.集合key部分了。

无序不可向重复

5、TreeSet

1、TreeSet集合底层实际上是一个TreeMap
2、TreeMap集合底层是一个二叉树。
3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合。

重点:自定义类型怎么排序

对自定义的类型来说,TreeSet可以排序吗?不可以,因为类型之间的大小规则没有说明。

自定义规则 可以进行排序:

1、放在集合中的元素实现java.lang.Comparable接口。重写compareTo方法

class student implements Comparable<student>{
    int age;

    public student(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "student{" +
                "age=" + age +
                '}';
    }
    @Override
    public int compareTo(student o) {
        return this.age-o.age;
    }
}

2、在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。简单

TreeSet<B> set1=new TreeSet<>(new Comparator<B>() {
    @Override
    public int compare(B o1, B o2) {
        return o2.age- o1.age;
    }
});

Comparable和Comparator怎么选择呢?
当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。
如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。

Comparator接口的设计符合OCP原则。

6、hashmap:非线程安全的

1、HashMap集合底层是哈希表/散列表的数据结构。

​ 哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)

2、哈希表是一个怎样的数据结构呢?
哈希表是一个数组和单向链表的结合体。
数组:在查询方面效率很高,随机增删方面效率很低。
单向链表:在随机增删方面效率较高,在查询方面效率很低。
哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
3、HashMap集合底层的源代码:
public class HashMap{
// HashMap底层实际上就是一个数组。(一维数组)
Node<K,V>[] table;
// 静态的内部类HashMap.Node
static class Node<K,V> {
final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
final K key; // 存储到Map集合中的那个key
V value; // 存储到Map集合中的那个value
Node<K,V> next; // 下一个节点的内存地址。
}
}

4、最主要掌握的是:
map.put(k,v)

第一步:先将k封装到node对象中

第二部:调用k的hashcode方法得到哈希值,根据哈希算法得到数组的下标,如果下标上有链表,比较k值,返回true则覆盖,返回false则插入到链表的末尾

​ v = map.get(k)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvRZaVxD-1645954130482)(F:\zhuomian\JAVA\09-JavaSE进阶每章课堂画图\05-集合\008-哈希表或者散列表数据结构.png)]

​ 以上这两个方法的实现原理,是必须掌握的。
5、HashMap集合的key部分特点: 无序,不可重复。
为什么无序? 因为不一定挂到哪个单向链表上。
不可重复是怎么保证的?equals方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。

放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。

6、哈希表HashMap使用不当时无法发挥性能!
假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们成为:散列分布不均匀。
什么是散列分布均匀?假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。
假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。
散列分布均匀需要你重写hashCode()方法时有一定的技巧。
7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
8、HashMap集合的默认初始化容量是16,默认加载因子是0.75
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

​ 重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
​ 这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

重写equals和hashcode方法:

1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!
equals方法有可能调用,也有可能不调用。
拿put(k,v)举例,什么时候equals不会调用?
k.hashCode()方法返回哈希值,
哈希值经过哈希算法转换成数组下标。
数组下标位置上如果是null,equals不需要执行。

2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
equals方法返回true表示两个对象相同,在同一个单向链表上比较。
那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
所以hashCode()方法的返回值也应该相同。

4、终极结论:
放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

5、对于哈希表数据结构来说:
如果o1和o2的hash值相同,一定是放到同一个单向链表上。
当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

6、如果链表中的元素超过8个以后,哈希链表就会变成二叉树,小于六个以后就还会变成单链表,这样是为了提高检索的效率

7、初始容量是16,扩容后的容量是元容量的二倍。

8、 HashMap集合的key和value都是可以为null的。

7、hashtable

Hashtable的key可以为null吗?
Hashtable的key和value都是不能为null的。

Hashtable方法都带有synchronized:线程安全的。
线程安全有其它的方案,这个Hashtable对线程的处理
导致效率较低,使用较少了。

Hashtable和HashMap一样,底层都是哈希表数据结构。
Hashtable的初始化容量是11,默认加载因子是:0.75f
Hashtable的扩容是:原容量 * 2 + 1

8、properties

目前只需要掌握Properties属性类对象的相关方法即可。
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
Properties被称为属性类对象。Properties是线程安全的。

两个方法:

存:pro.setProperty(k,v)

取:pro.getProperty(k)

泛型:

1、定义:

1、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)

2、优缺点:

2、使用了泛型好处是什么?
第一:集合中存储的元素类型统一了。
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!

3、泛型的缺点是什么?
导致集合中存储的元素缺乏多样性!
大多数业务中,集合中元素的类型还是统一的。所以这种泛型特性被大家所认可。

3、自动类型推断机制。(又称为钻石表达式)

例子:

// ArrayList<这里的类型会自动推断>(),前提是JDK8之后才允许。
// 自动类型推断,钻石表达式!
List myList = new ArrayList<>();

4、自定义泛型

自定义泛型的时候,<> 尖括号中的是一个标识符,随便写。
java源代码中经常出现的是: 和
E是Element单词首字母。
T是Type单词首字母。

例子:ArrayList list=new ArrayList<>;

四、所有的实现类:

总结(所有的实现类):

ArrayList:底层是数组。

LinkedList:底层是双向链表。

Vector:底层是数组,线程安全的,效率较低,使用较少。

HashSet:底层是HashMap,放到HashSet,集合中的元素等同于放到HashMap.集合key部分了。

TreeSet:底层是TreeMap,放到 TreeSet,集合中的元素等同于放到 TreeMap,集合key部分了。

HashMap:底层是哈希表。

Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。

Properties:是线程安全的,并且 key和value只能存储字符串 Stringo

TreeMap:底层是二叉树。TreeMap集合的 key可以自动按照大小顺序排序。

List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。可重复:存进去1,可以再存储一个1.

set集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出的顺序不一定相同。另外set集合中元素没有下标。不可重复:存进去1,不能再存储1了。

SortedSet ( SortedMap)集合存储元素特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出的顺序不一定相同。另外set集合中元素没有下标。不可重复:存进去1,不能再存储1了。v
可排序:可以按照大小顺序排列。

Map集合的 key,就是一个set集合。
往set集合中放数据,实际上放到了Map 集合的 key部分。

五、collection常用方法

boolean add(Object e) 向集合中添加元素

int size() 获取集合中元素的个数

void clear() 清空集合

boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false

那么它在底层是怎么判断集合中是否包含某个元素的呢?
调用了equals方法进行比对。
equals方法返回true,就表示包含这个元素。

User u1 = new User("jack");
// 加入集合
c.add(u1);
User u2 = new User("jack");
// 没有重写equals之前:这个结果是false
//System.out.println(c.contains(u2)); // false
// 重写equals方法之后,比较的时候会比较name。
System.out.println(c.contains(u2)); // true

boolean remove(Object o) 删除集合中的某个元素。

也是调用了equals方法

 User u1 = new User("jack");
// 加入集合
c.add(u1);
User u2 = new User("jack");
c.remove(u2);
System.out.println(c.size()); // 0

boolean isEmpty() 判断该集合中元素的个数是否为0

Object[] toArray() 调用这个方法可以把集合转换成数组。【作为了解,使用不多。】

存放在一个集合中的类型,一定要重写equals方法。

六、迭代器

1、定义:

1、collection重写父接口中的iterator方法,返回iterator对象。

2、是所有Collection通用的一种方式。在所有的Collection以及子类中使用。不能在Map集合中使用。

3、存进去是什么类型,取出来还是什么类型。

4、只不过在输出的时候会转换成字符串。因为这里println会调用toString()方法。

2、步骤:

第一步:获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();
第二步:通过以上获取的迭代器对象开始迭代/遍历集合。

3、iterator迭代器对象中两个方法:

boolean hasNext()如果仍有元素可以迭代,则返回 true。

Object next() 返回迭代的下一个元素。

4、例子:

it是集合对象

    while(it.hasNext()){
        Object obj = it.next();
        System.out.println(obj);
    }

5、注意:

1、当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常:java.util.ConcurrentModificationException

2、在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,
不要使用集合自带的remove方法删除元素。

3、迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)。直接通过集合去删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同。)

七、foreach

语法:for(元素类型 变量名 : 数组或集合)

foreach有一个缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。

八、集合便利的三种方式:

//遍历
//1、使用迭代器
Iterator<String> it1=list.iterator();
while (it1.hasNext()){
    System.out.println(it1.next());
}
System.out.println("================");

//使用下标
for (int i = 0; i < list.size() ; i++) {
    System.out.println(list.get(i));

}
System.out.println("================");

//使用foreach
for (String a:list
     ) {
    System.out.println(a);
}

不能为null的。

Hashtable方法都带有synchronized:线程安全的。
线程安全有其它的方案,这个Hashtable对线程的处理
导致效率较低,使用较少了。

Hashtable和HashMap一样,底层都是哈希表数据结构。
Hashtable的初始化容量是11,默认加载因子是:0.75f
Hashtable的扩容是:原容量 * 2 + 1

8、properties

目前只需要掌握Properties属性类对象的相关方法即可。
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
Properties被称为属性类对象。Properties是线程安全的。

两个方法:

存:pro.setProperty(k,v)

取:pro.getProperty(k)

泛型:

1、定义:

1、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)

2、优缺点:

2、使用了泛型好处是什么?
第一:集合中存储的元素类型统一了。
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!

3、泛型的缺点是什么?
导致集合中存储的元素缺乏多样性!
大多数业务中,集合中元素的类型还是统一的。所以这种泛型特性被大家所认可。

3、自动类型推断机制。(又称为钻石表达式)

例子:

// ArrayList<这里的类型会自动推断>(),前提是JDK8之后才允许。
// 自动类型推断,钻石表达式!
List myList = new ArrayList<>();

4、自定义泛型

自定义泛型的时候,<> 尖括号中的是一个标识符,随便写。
java源代码中经常出现的是: 和
E是Element单词首字母。
T是Type单词首字母。

例子:ArrayList list=new ArrayList<>;

四、所有的实现类:

总结(所有的实现类):

ArrayList:底层是数组。

LinkedList:底层是双向链表。

Vector:底层是数组,线程安全的,效率较低,使用较少。

HashSet:底层是HashMap,放到HashSet,集合中的元素等同于放到HashMap.集合key部分了。

TreeSet:底层是TreeMap,放到 TreeSet,集合中的元素等同于放到 TreeMap,集合key部分了。

HashMap:底层是哈希表。

Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。

Properties:是线程安全的,并且 key和value只能存储字符串 Stringo

TreeMap:底层是二叉树。TreeMap集合的 key可以自动按照大小顺序排序。

List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。可重复:存进去1,可以再存储一个1.

set集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出的顺序不一定相同。另外set集合中元素没有下标。不可重复:存进去1,不能再存储1了。

SortedSet ( SortedMap)集合存储元素特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出的顺序不一定相同。另外set集合中元素没有下标。不可重复:存进去1,不能再存储1了。v
可排序:可以按照大小顺序排列。

Map集合的 key,就是一个set集合。
往set集合中放数据,实际上放到了Map 集合的 key部分。

五、collection常用方法

boolean add(Object e) 向集合中添加元素

int size() 获取集合中元素的个数

void clear() 清空集合

boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false

那么它在底层是怎么判断集合中是否包含某个元素的呢?
调用了equals方法进行比对。
equals方法返回true,就表示包含这个元素。

User u1 = new User("jack");
// 加入集合
c.add(u1);
User u2 = new User("jack");
// 没有重写equals之前:这个结果是false
//System.out.println(c.contains(u2)); // false
// 重写equals方法之后,比较的时候会比较name。
System.out.println(c.contains(u2)); // true

boolean remove(Object o) 删除集合中的某个元素。

也是调用了equals方法

 User u1 = new User("jack");
// 加入集合
c.add(u1);
User u2 = new User("jack");
c.remove(u2);
System.out.println(c.size()); // 0

boolean isEmpty() 判断该集合中元素的个数是否为0

Object[] toArray() 调用这个方法可以把集合转换成数组。【作为了解,使用不多。】

存放在一个集合中的类型,一定要重写equals方法。

六、迭代器

1、定义:

1、collection重写父接口中的iterator方法,返回iterator对象。

2、是所有Collection通用的一种方式。在所有的Collection以及子类中使用。不能在Map集合中使用。

3、存进去是什么类型,取出来还是什么类型。

4、只不过在输出的时候会转换成字符串。因为这里println会调用toString()方法。

2、步骤:

第一步:获取集合对象的迭代器对象Iterator
Iterator it = c.iterator();
第二步:通过以上获取的迭代器对象开始迭代/遍历集合。

3、iterator迭代器对象中两个方法:

boolean hasNext()如果仍有元素可以迭代,则返回 true。

Object next() 返回迭代的下一个元素。

4、例子:

it是集合对象

    while(it.hasNext()){
        Object obj = it.next();
        System.out.println(obj);
    }

5、注意:

1、当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常:java.util.ConcurrentModificationException

2、在迭代元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,
不要使用集合自带的remove方法删除元素。

3、迭代器去删除时,会自动更新迭代器,并且更新集合(删除集合中的元素)。直接通过集合去删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同。)

七、foreach

语法:for(元素类型 变量名 : 数组或集合)

foreach有一个缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。

八、集合便利的三种方式:

//遍历
//1、使用迭代器
Iterator<String> it1=list.iterator();
while (it1.hasNext()){
    System.out.println(it1.next());
}
System.out.println("================");

//使用下标
for (int i = 0; i < list.size() ; i++) {
    System.out.println(list.get(i));

}
System.out.println("================");

//使用foreach
for (String a:list
     ) {
    System.out.println(a);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java笔记是由北京大学青鸟教育推出的一款专门针对Java语言的学习工具。它以全面、系统、实践为特点,通过详细的代码示例和清晰的讲解,帮助学习者全面掌握Java编程语言Java笔记采用了线上与线下相结合的学习模式。学员可以通过手机、平板电脑、电脑等设备在线学习,还可以在学习过程中随时记录自己的学习笔记。同时,北大青鸟还为学员提供线下实践环境,学员可以在实验室里亲自动手实践所学知识,加深理解和应用。 Java笔记的内容非常全面,包括了Java语言的基本语法、面向对象编程、异常处理、流操作、多线程、数据库操作等众多知识点。除了理论知识,Java笔记还提供了大量的实例代码,可供学员参考和模仿。这样的学习方式既帮助学员理解Java的基本概念,又能让他们运用所学知识解决实际问题。 与此同时,Java笔记还注重学员的互动交流。在学习过程中,学员可以利用笔记功能记录学习心得和疑惑,还可以在论坛上与其他学员进行讨论和交流。这种互动形式既能促进学员之间的学习互助,也能更好地帮助学员理解和应用所学知识。 总之,Java笔记是北大青鸟推出的一款专注于Java语言学习的工具,通过系统的课程设置、丰富的实例代码和互动交流的方式,帮助学员全面掌握Java编程知识,提升编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值