黑马阿玮Java笔记

输入

步骤一:导包

import java.util.Scanner;

步骤二:创建对象

Scanner sc = new Scanner(System.in); 只有 sc 是变量名,可以变,其他的都不允许变

只用写一遍就可以

步骤三:接收数据

int i = sc.nextInt(); 只有 i 是变量名,可以变,其他的都不可以变

隐式转换和强制转换

隐式转换

把取值范围小的数值,转成取值范围大的数据,小的会先提升为大的,再进行运算

byte short char 三种类型的数据再运算的时候,都会直接先提升为 int ,然后再进行运算

强制转换

如果把一个取值范围大的数值,赋值给取值范围小的变量,是不允许直接赋值的,需要强制转换

格式:目标数据类型 变量名 = (目标数据类型)被强制转换的数据;

字符串

字符串的 + 操作

当 + 操作中出现字符串时,这个 + 是字符串连接符,不是运算符,会将前后的数据进行拼接,产生一个新的字符串

如:“123”+123

结果是 : “123123”

连续进行 + 操作时,从左到右逐个执行

逻辑运算符

&

逻辑与:并且,两边都为真,结果才是真(1)

|

逻辑或:或者,两边都为假,结果才是假(0)

^

逻辑异或:相同为 false(1),不同为 true(0)

!

逻辑非:取反

短路运算符

符号 作用 说明

&& 短路与 结果和&相同,但是有短路效果

|| 短路或 结果和|相同,但是有短路效果

短路效果:

当左边的表达式能确定最终结果时,那么右边的就不会参与运算了

注意:

  • & 和 | ,无论左边 true 或 false ,右边都要执行

  • && 或 ||,如果左边能确定整个表达式的结果,右边不执行

&&

左边为 false ,右边不管真假,整个表达式结果一定是 false

||

左边为 true ,右边不管真假,整个表达式结果一定是 true

if

第一种情况

if(关系表达式){

​ 语句体;

}

第二种情况

if(关系表达式){

​ 语句体;

} else{

​ 语句体;

}

第三种情况

if(关系表达式){

​ 语句体;

} else if(关系表达式){

​ 语句体;

}

else{

​ 语句体;

}

switch 语句

switch(表达式){

​ case 值1:

​ 语句体1;

​ break;

​ case值2:

​ 语句体2;

​ break;

​ default:

​ 语句体n+1;

​ break;

}

如果所有 case 后面的值和表达式的值都不匹配,就会执行 default 里面的语句体,然后结束整个 switch 语句

for

for(初始化语句;条件判断语句;条件控制语句){

​ 循环体语句;

}

数组

格式一:数据类型[] 数组名

例: int[] array

格式二:数据类型 数组名[]

例: int array[]

静态初始化

数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…};

例: int array[] = new int[]{1,2,3,4,…};

​ double[] array = new double[]{11.1,22.2,33.3};

简化格式:数据类型[] 数组名 = {元素1,元素2,元素3,…};

手动指定数组元素,系统会根据元素个数,计算出数组长度(需求已明确要操作的具体数据,直接静态化初始即可)

地址

获取数组里的某个元素

格式: 某个变量 = 数组名[索引];

把数据存储到数组中

格式: 数组名[索引] = 具体数据;

在 Java 中,数组的一个长度属性为 length , 例:array.length;

数组的动态初始化

String arr[] = new String[50];

手动指定数组长度,由系统给出默认初始化值(值明确元素个数,不明确具体数值,推荐使用动态初始化)

Java 内存分配

  • 栈:方法运行时使用的内存,比如 main 方法运行,进入方法栈中执行

  • 堆:存储对象或数组,new 来创建,都存储再堆内存

  • 方法区:存储可以运行的 class 文件

  • 本地方法栈: jvm在使用操作系统功能的时候使用,和我们无关

  • 寄存器:给CPU使用,和我们开发无关

当两个数组指向同一个小空间时,其中一个数组对小空间的值发生改变(比如赋值),则另外的数组再次访问这个时,值都是被修改过之后的了

方法

方法 是程序中最小的执行单元

如:main 方法(主方法)

作用:

  • 提高代码的复用性

  • 提高代码可维护性

不带参数的方法定义和调用

定义:

public static void 方法名()

{

​ 方法体(代码);

}

调用:

方法名();

先定义,后调用!

带参数的方法定义和调用

定义:

public static void 方法名(参数1,参数2,…)

{

​ …;

}

调用

方法名(参数1,参数2,…);

形参和实参

形参

全称形式参数,是指方法定义中的参数

实参

全称实际参数,方法调用中的参数

形参和实参必须一一对应

返回值

方法的返回值就是方法运行的最终结果

定义:

public static 返回值类型 方法名(参数)

{

​ …;

​ return 返回值;

}

调用:

  • 直接调用:方法名(实参);

  • 赋值调用:整数类型 变量名 = 方法名(实参);

  • 输出调用:System.out.println(方法名(实参));

方法的重载

在同一个类中,定义了多个同名的方法,这些方法具有同种功能,每个方法具有不同参数类型或参数个数

Java 虚拟机会通过参数的不同区分同名的方法

this 关键字

成员变量

定义在方法的外边,类的里面

如:

public class gf()

{

​ private int age; //这里的 age 是成员变量

}

局部变量

定义在方法里的变量

如:

public void method()
{

​ int age = 10; //这里的 age 就是局部变量

​ sout(age);

}

使用 this 关键字就会使用成员变量 age ,而不是局部变量 age。这个关键字主要用于重名的变量

构造方法

也叫 构造器、构造函数

作用:在创建对象时给成员变量进行初始化(即赋值)

构造方法格式

  • 方法名与类名相同,大小写也一致
  • 没有返回值类型,连 void 也没有
  • 没有具体的返回值

​ 创建对象时由虚拟机调用,不能手动调用构造方法

​ 每创建一次对象,就会调用一次构造方法

如:创建了一个 Student 类,在创建 Student 类对象时,就会自动调用构造方法 Student()

public Student(){

​ …;

}

注意事项:

  • 如果没有定义构造方法,系统将会给出一个默认的无参构造方法
  • 如果定义了构造方法,系统将不会提供默认的构造方法
  • 带参构造方法,和无参构造方法,方法名相同,参数不同,这叫重载
  • 无论是否适用,都手动书写无参构造方法,和带全部参数的构造方法

总结:构造方法的作用就是用来初始化对象的状态

标准 JavaBean

  • 类名需要见名知意
  • 成员变量要用 private 修饰
  • 至少提供两个构造方法
  1. 无参
  2. 有参
  • 成员方法
  1. 提供每一个成员变量对应的 setXXX()/getXXX()
  2. 如果还有其他行为,也要写上

快捷键: alt + ins / alt + fn + ins

插件:PTG

API

应用程序接口

就是别人已经写好的东西,自己不需要再写,直接使用就行

String

String 的比较

== 比较

基本数据类型

比的是 数据值

引用数据类型

比的是 地址值

内容比较

equals 方法

String对象a.equals(String 对象b)

内容完全一样结果才是 true ,否则为 false

equalsIgnoreCase 方法

String对象a.equalsIgnoreCase(String 对象b)

内容忽略大小写比较

StringBuilder 类

StringBuilder 构造方法

public StringBuilder()

创建一个空白可变字符串对象,不含有任何内容

public StringBuilder(String str)

根据字符串的内容,来创建可变字符串对象

StringBuilder 常用方法

public StringBuilder append(任意类型)

添加数据,并返回对象本身

public StringBuilder reverse()

反转容器中的内容

public int length()

返回字符串的长度(字符串出现的个数)

public int capacity()

返回字符串的容量

public String toString()

通过 toString() 可以实现把 StringBuilder 转换为 String

StringJoiner 类

StringJoiner 构造方法

public StringJoiner (间隔符号)

创建一个 StringJoiner 对象,指定拼接时的间隔符号

public StringJoiner (间隔符号,开始符号,结束符号)

创建一个 StringJoiner 对象,指定拼接时的间隔符号、开始符号、结束符号

StringJoiner 常用方法、

public StringJoiner toString()

通过 toString() 可以实现把 StringJoiner 转换为 String

public StringJoiner add(“字符串”)

类似于 StringBuilder 里的 append 方法,给原始的一个 StringJoiner 对象后面添加一个字符串

字符串的拼接

存在变量时

JDK8 以前,如果有变量,系统会自动创建一个 StringBuilder 对象,然后再调用 append 方法完成拼接,拼接后,在调用 toString 方法转换为 String 类型,但 toString 方法会直接 new 一个字符串对象,占用内存

JDK8 以后,系统会预估字符串拼接之后的大小,把要拼接的内容都放在数组中,此时产生一个新的字符串

不存在变量时

字符串直接相加,编译之后就是拼接之后的结果,会 复用 串池中的字符串

所以两个 String 之间用 == 结果是 true

总结:把要拼接的内容放在 StringBuilder 里,不会创建很多无用空间,节约内存

StringBuilder 对象默认创建一个最大长度为 16 ,扩容之后的容量为 老容量 * 2 + 2 的字节数组

集合

创建集合对象

泛型:限定集合中存储数据的类型

先提前导包: import java.util.ArrayList;

JDK7 以前

ArrayList< String > list = new ArrayList<>();

JDK7 以后

ArrayList< String > list = new ArrayList<>();

打印的时候会用 [] 把所有内容进行包裹

成员方法

E:数据的类型,如:String

boolean add(E e)

添加元素,返回值表示是否添加成功

boolean remove(E e)

删除指定元素,返回值表示是否删除成功

E remove(int index)

删除指定索引的元素,返回被删除元素

E set(int index,E e)

修改指定索引下的元素,返回原来的元素

E get(int index)

获取指定索引的元素

int size()

集合的长度,也就是集合中元素的个数

Static

Static 表示静态,是 Java 中的一个修饰符,可以修饰成员方法,成员变量

被 Static 修饰的成员变量,叫静态变量

被 Static 修饰的成员方法,叫静态方法

静态变量

静态区是存储静态变量的空间,在堆内存中。

特点

被该类所有对象共享 (即全局变量)

不属于对象,属于类

随着类的加载而加载,优先于对象出现

调用方式

  • 类名调用(推荐)

Student.teachername = “…”;

  • 对象名调用

s1.teachername = “…”;

s2.teachername = “…”;

s3…

静态方法

工具类:帮助我们做一些事情的,里面有专门用来解决事情的方法,但是不描述任何事物的类,由于方法都是静态方法(Static),因此不需要特地用这个类来 new 一个对象再用这个对象进行各种方法,直接引用 类.方法 就行,且构造方法要私有化(private)

特点:

  • 多用在测试类和工具类中

  • JavaBean 类中很少会用

调用方式:

  • 类名调用(推荐)

  • 对象名调用

Static 注意事项

  • 静态方法 只能 访问静态变量和静态方法

  • 非静态方法 可以 访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法

  • 静态方法中是没有 this 关键字

this: 表示当前方法调用者的地址值

继承

Java 提供了一个关键字 extends ,用这个关键字,我们可以让一个类和另一个类建立起继承关系

public class Student extends Person{}

Student 称为 子类(派生类),Person 称为 父类(基类或超类)

使用继承的好处

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性

  • 子类可以再父类的基础上,增加其他功能,使子类更强大

当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码

子类能继承的父类内容如下:

构造方法 public 不能 private 不能

成员变量 public 能 private 能

成员方法 public 能 private 不能

继承的特点

  • Java 只能单继承,不能多继承,但能多层继承

  • 所有类都直接或间接继承于 Object 类

  • this 调用:就近原则 super 调用:直接找父类

虚方法表

随着一个个继承的子类而不断增加方法的数量,其中的方法不能是 private 、 static 、 final 属性

作用:调用方法时可以不用一级一级去调用父类的方法,而是直接调用虚方法表里的方法,提高了效率

继承中 成员变量

访问特点

就近原则

先在局部找,本类成员位置找,父类成员位置找,逐级往上

继承中 成员方法

访问特点

就近原则

先在局部找,本类成员位置找,父类成员位置找,逐级往上

方法的重写

当父类不能满足子类现在的需求,需要进行方法重写

书写格式

在继承体系中,子类出现了和父类一模一样的方法声明,我们就称子类这个方法是重写的方法

@Override 重写注解

  • @Override 是放在重写后的方法上,校验子类重写时语法是否正确

  • 加上注解后如果有红色波浪,表示语法错误

  • 建议重写方法都加 @Override 注解,安全又优雅

如:

@Override

public void xxx(){…};

注意事项和要求

  • 重写方法的名称、形参列表 必须与父类中的一致

  • 子类重写父类方法时,访问权限必须大于等于父类 (空着不写 < protected < public )

  • 子类重写父类方法时,返回值类型子类必须小于等于父类

  • 重写方法时尽量与父类保持一致

  • 私有方法不能被重写,只有被添加到虚方法表中的方法才能被重写

继承中 构造方法的访问特点

  • 父类中的构造方法不会被子类继承

  • 子类中所有的构造方法默认先访问父类中的无参构造(对父类进行初始化),再执行自己

原因:

  1. 子类再初始化时,有可能会使用到父类中的构造,如果父类没有完成初始化,子类将无法使用父类的数据

  2. 子类初始化之前,一定要用父类构造方法完先完成父类数据空间的初始化

如何调用父类构造方法?

  • 子类构造方法的第一行语句默认都是 : super(); 不写也存在,且必须在第一行

  • 如果想调用父类有参构造,必须手写 super 进行调用

this super 使用总结

this : 理解为一个变量,表示当前方法调用者的地址值

super : 代表父类存储空间

关键字				 访问成员变量				 	访问成员方法			 	 访问构造方法	
    
this               this.成员变量			 this.成员方法(...)			  this(...) 
    			  访问本类成员变量				访问本类成员方法			访问本类构造方法
    

super			   super.成员变量			 super.成员方法(...)		  super(...)
    			  访问父类成员变量				访问父类成员变量			访问父类构造方法

多态

表现形式

父类类型 对象名称 = 子类对象;

前提

  • 有继承关系

  • 有父类引用指向子类对象

  • 有方法重写

好处

使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利

多态调用成员的特点

变量调用

编译看左边,运行也看左边

编译

javac 编译代码时,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有,编译失败

运行

java 运行代码时,实际获取的就是左边父类中成员变量的值

方法调用

编译看左边,运行看右边

编译

javac 编译代码时,会看左边的父类中有没有这个方法,如果有,编译成功,没有则失败

运行

java 运行代码时,实际获取的就是右边子类中的方法

成员变量

在子类对象中,会把父类的成员变量也继承下的

父:age 子:age

成员方法

如果在子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的

多态的优势

  • 多态形式下,右边对象可以实现解耦合,便于拓展和维护

​ Person p = new Student();

​ p.work(); //业务逻辑发生改变时,后续代码无需修改

  • 定义方法时,使用父类型作为参数,可以接收所有子类对象,体现多态的拓展性与便利

多态的弊端

  • 不能调用子类的特有变量和方法

解决方法:变回子类类型

Dog d = (Dog) a;

此时 d 就是 Dog 类的对象,可以调用 Dog 类特有变量和方法

转换类型与真实对象类型不一致会报错

判断对象的类名

if(a instanceof Dog){ //判断 a 是不是 Dog 类

​ Dog d = (Dog) a;

​ d.xxx(); //调用 Dog 类特有方法(或变量)

} else if (a instanceof Cat){

​ Cat c = (Cat) a;

​ c.xxx(); //调用 Cat 类特有方法(或变量)

}else{

​ sout(“没有这个选项,无法转换”);

}

新特性

先判断 a 是否为 Dog 类型,如果是,则强制转成 Dog 类型,转换之后变量名为d,如果不是,则不强转,结果直接是false

if(a instanceof Dog d){

​ d.xxx();

}else if (a instanceof Cat c){

​ c.xxx();

}else{

​ sout(“没有这个选项,无法转换”);

}

导包

包就是文件夹,用来管理各种不同功能的 Java 类,方便后期维护

包名规则

域名反写 + 包的作用,需要全部英文小写,见名知意

使用其他类的规则

  • 使用同一个包的类时,不需要导包

  • 使用 Java.lang 包中的类时,不需要导包

  • 其他情况需要导包

  • 如果同时使用两个包中的同名类,需要用全类名

final 关键字

最终的 —> 不可被修改的

用来修饰 方法、类、变量

修饰方法

表明该方法是最终方法,不能被重写

修饰类

表明该类是最终类,不能被继承

修饰变量

被修饰的叫做常量,只能被赋值一次

常量

常量一般作为系统的配置信息,方便维护,提高可读性

命名规则
  • 单个单词

    全部大写

  • 多个单词

​ 全部大写,单词之间用下划线隔开

细节

final 修饰的变量是基本类型:那么变量存储的数据值不能发生改变

final double PI = 3.141; //这里的 PI 的值是不能被修改的

final 修饰的是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变

final Student S = new Student(name,age);

//S = new Student(); //这里是错误的,变量存储的地址值不能发生改变

S.setname = “xxx”; //内部可以更改

S.setage = xx; //内部可以更改

用 final 修饰数组,数组中各索引的值可以被更改

权限修饰符

修饰符			同一个类中		  同一个包其他类		 不同包下的子类        不同包下的无关类 
    
private		    √
    
空着不写    	 √					 √
    
protected		√					√					√
    
public			√					√					√					√

使用规则

一般只使用 private 和 public

  • 成员变量私有

  • 方法公开

**特例:**如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有

代码块

构造代码块

抽取构造方法中的重复代码

在构造函数之前就会运行,很少用了

静态代码块

格式

static{}

特点

需要通过 static 关键字修饰,随着类的加载而加载,并且自动触发,只执行一次

使用场景

在类加载时,做一些数据初始化的时候使用,比如以前加载一个集合,在里面添加一些对象什么的

抽象类

抽象方法

将共性的行为(方法)抽取到父类之后,由于每个子类的执行内容不一样,所以,在父类中不能确定具体的方法体,该方法就可以定义为抽象方法

抽象类

如果一个类中存在抽象方法,那么该类就必须声明为抽象类

定义格式

抽象方法的定义格式

public abstract 返回值类型 方法名(参数);

抽象类的定义格式

public abstract class 类名{}

注意事项

  • 抽象类不能实例化(不能创建对象)

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

  • 可以有构造方法

  • 抽象类的子类

​ 要么重写抽象类中的所有抽象方法(即强制每一个子类重写和父类不一样的方法)

​ 要么是抽象类

抽象类照样可以写构造方法,作用:当创建子类对象时,给属性赋值

抽象类可以规范化代码,把每个子类的同一个方法(比如 吃(eat) 方法) 定义成同一个名字,不会混乱

接口

接口是对 行为 的抽象,与抽象类和抽象方法不同,抽象方法只能用在父类之中,作用于这个父类的子类,但定义接口后,可以用于任意父类的子类,更灵活

接口的定义和使用

  • 接口用关键字 interface 来定义

​ public interface 接口名{}

  • 接口不能实例化

​ 不能创建对象

  • 接口和类之间是实现关系,通过 implements 关键字表示

​ public class 类名 implements 接口名 {}

  • 接口的子类 (实现类)

​ 要么重写接口中的所有抽象方法

​ 要么是抽象类

注意1:接口和类的实现关系,可以单实现,也可以多实现

​ public class 类名 implements 接口名1,接口名2 {}

注意2:实现类还可以在继承一个类的同时实现多个接口

​ public class 类名 extends 父类 implements 接口名1,接口名2 {}

接口中成员的特点

成员变量

只能是常量

默认修饰符:public static final (没写也会自动加上)

构造方法

没有

成员方法

只能是抽象方法

默认修饰符:public abstract

JDK7以前:接口中只能定义抽象方法

JDK8:接口中可以定义有方法体的方法

JDK9:接口中可以定义私有方法

注意

  • 接口可以单继承,也可以多继承,而且接口没有构造方法

  • 测试类在实现接口时,如果接口是继承父类接口的子类,那么测试类需要把继承下来的成员变量和方法还有正在实现的这个接口的成员变量和方法都重写一遍

类和类的关系

继承关系,只能单继承,不能多继承,但是可以多层继承

类和接口的关系

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

接口和接口的关系

继承关系,可以单继承,也可以多继承

JDK8 开始接口中新增的方法

  • 接口中可以定义有方法体的方法

  • 允许在接口中定义默认方法,需要使用关键字 default 修饰

作用:解决接口升级的问题

接口中默认方法的定义格式

格式

public default 返回值类型 方法名(参数列表){}

范例

public default void show()

​ {

​ …;

​ }

注意

  • 默认方法不是抽象方法,所以不强制重写,但如果被重写,重写的时候去掉 default 关键字

  • public 可以省略,dafault 不可省略

  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写

接口中静态方法的定义格式

格式

public static 返回值类型 方法名(参数列表)

​ {

​ …;

​ }

范例

public static void show(){…;}

注意

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

Inter.show();

  • public 可以省略,static 不可省略

JDK9新增的方法

接口中私有的定义格式

格式1

private 返回值类型 方法名(参数列表){…;}

范例1

private void show(){…;}

减少代码的重复,遇到重复的直接调用专门记录重复代码的方法就行

格式2

private static 返回值类型 方法名(参数列表){…;}

范例2

private static void method(){…;}

为了给前面的 static 方法服务,可以在前面的 static 方法里调用专门记录重复代码的方法,但因为前面的是 static 方法,所以自己必须也加上 static

如:

public interface InterA{

​ public static void show1(){

​ sout(“show1方法”);

​ show4();

​ }

​ public static void show2(){

​ sout(“show2方法”);

​ show4();

​ }

​ //普通的私有方法,给默认方法服务的

​ private void show(){

​ sout(“重复代码”);

​ }

​ private static void show4(){

​ sout(“重复代码”);

​ }

}

内部类

在一个类里面,再定义一个类

如:在A类的内部定义B类,B类就被称为内部类

例:

public class Car{

	String Name;

	int carAge;

	String carColor;

		class Engine{

			String engineName;

			int engineAge;

	}
}

内部类的分类

成员内部类(了解)

  • 写在成员位置的,属于外部类的成员

  • 可以被一些修饰符所修饰,如:private 默认 protected public static

  • 在成员内部类里,JDK16 之前不能定义静态变量,JDK16 开始才能定义静态变量

创建成员内部类对象
方法一

在外部类中编写方法,对外提供内部类对象

public class Outer{
    
    String name;
    
    private class Inner{
        
    }
    
    public Inner getInstance(){
        return new Inner();
    }
    
}

/*测试类*/

Outer o  =  new Outer();

/*调用*/

Object inner  =  o.getInstance();	//利用多态(类 Inner 父类是 Object)
或:
sout(o.getInstance());
方法二

直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

Outer.Inner oi = new Outer().new Inner();
就近原则获取变量
public class Outer{
    private int a =  10;
    
    class Inner{
        private int a = 20;
        
        public void show(){
            int a = 30;
            sout(Outer.this.a);		//10,调用外部类的 this 关键字
            sout(this.a);			//20,调用方法所属类的 a
            sout(a);				//30,调用方法里的a
        }
    }
}

静态内部类(了解)

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象

public class Car{

	String Name;

	int carAge;

	String carColor;

		static class Engine{

			String engineName;

			int engineAge;

	}
}
创建静态内部类对象

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

Outer.Inner oi	=	new Outer.Inner();
调用静态方法的格式

外部类名.内部类名.方法名();

Outer.Inner.show();

局部内部类(了解)

  • 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量

  • 外界是无法直接使用,需要在方法内部创建对象并使用

  • 该类可以直接访问外部类成员,也可以访问方法内的局部变量

public class Outer{
    int b = 20;
    public void show(){
        int a = 10;
    }
    
    class Inner{
        
    	String name;
        int age;
        
        public void method1(){
            sout(a);
            sout(b);
            sout("局部内部类中的 method1 方法");
        }
        
        public static void method2(){
            sout("局部内部类中的 method2 方法");
        }
    }
    
    Inner i = new Inner();
    sout(i.name);
    sout(i.age);
    i.method1;
    Inner.method2;
    
}

匿名内部类(掌握)

本质上就是隐藏了名字的内部类

格式
new 类名或者接口名(){
    重写方法;
};
举例
new Inter(){
    public void show(){...;}
}

Arrays

方法名																说明
    
public static String toString(数组)								把数组拼接成一个字符串
    
public static int binarySearch(数组,查找的元素)					二分法查找元素
    
public static int[] copyOf(原数组,新数组长度)					    拷贝数组
    
public static int[] copyOfRange(原数组,起始索引,结束索引)			 拷贝数组(指定范围)
    
public static void fill(数组,元素)								  填充数组
    
public static void sort(数组)										按默认方式进行数组排序
    
public static void sort(数组,排序规则)							 按指定规则排序

sort

第二个参数是一个接口,调用时需要传递实现这个接口的类的对象,作为排序规则

这个实现类只使用一次,可以用匿名内部类方法

sort 底层原理

  • 利用插入排序 + 二分查找的方法进行排序的

  • 默认把 0 索引数据当作有序序列,1 索引到最后是无序序列

  • 遍历无序的序列得到里面的每一个元素,假设当前便利得到的元素是A

  • 把A元素往有序序列中插入,插入时用二分查找确定插入点

  • 拿着A元素,跟插入点进行比较,比较规则就是 compare 方法的方法体

  • 如果返回负数,拿着A继续跟前面的数据比较,如果返回正数,拿着A继续跟后面数据比较,如果返回0,拿着A跟后面数据比较

//o1 - o2:升序排列 
//o2 - o1:降序排列
Arrays.sort(arr,new Comparator<Integer>(){

	@Override
	public int compare(Integer o1,Integer o2){	// o1:表示无序序列中,遍历得到的每一个元素
        										// o2:表示有序序列中的元素
        return o1 - o2;
    }
    
});

Lambda 表达式

函数式编程

是一种思想特点,忽略面向对象的复杂语法,强调做什么,而不是谁去做

标准格式

() -> {

​ …;

}

():对应方法的形参

->:固定格式

{}:对应方法的方法体

Arrays.sort(arr,new Comparator<Integer>(){

	@Override
	public int compare(Integer o1,Integer o2){	// o1:表示无序序列中,遍历得到的每一个元素
        										// o2:表示有序序列中的元素
        return o1 - o2;
    }
    
});

可改写为:

//完整格式:
Arrays.sort(arr,(Integer o1,Integer o2) -> {
    return o1-o2;
});

//简写格式:
Arrays.sort(arr,(o1,o2) -> o1.length() - o2.length());

注意

  • Lambda 表达式可以用来简化匿名内部类的书写

  • Lambda 表达式只能简化函数式接口的匿名内部类的写法

  • 函数式接口:有且仅有一个抽象方法的接口叫函数式接口,接口上方可以加上 @FunctionalInterface 注解

  • 简写格式:

  • 小括号:数据类型可以省略,如果参数只有一个,小括号还可以省略

  • 大括号:如果方法体只有一行,return 分号 大括号 都可以省略

好处

Lambda 是一个匿名函数,可以理解为是一段可以传递的代码,可以写出更简洁、灵活的代码

集合

集合
单列集合
双列集合
Collection
List
Set
ArrayList
LinkedList
Vector
HashSet
TreeSet
LinkedHashSet

单列集合

Collection(祖宗接口)

所有单列集合都可以继承

方法名称										说明
    
public boolean add(E a)						   把给定对象添加到当前集合中
    
public void clear()						       清空集合中所有元素
 
public boolean remove(E e)					   把给定的对象在当前集合中删除
    
public boolean contains(Object obj)			   判断当前集合中是否包含给定的对象
    
public boolean isEmpty()					   判断当前集合是否为空
    
public int Size()							   返回集合中元素的个数/集合的长度

注意:

  • add(E a):

    如果往 List 系列集合中添加数据,会永远返回 true ,因为List系列的是允许元素重复的

    如果往 Set 系列集合添加数据,如果已存在会返回 false ,不存在才会返回 true

  • remove(E e):

    因为 Collection 里面定义的是共性的方法,所以不能通过索引进行删除,只能通过元素对象进行删除

  • contains():

​ 底层是通过 equals 方法判读的,所以如果集合中存储的自定义对象,如:Student 也想通过 contains 方法判断是否包含,在 javabean 类中一定要重写 equals 方法,因为原来的 equals 方法是判断地址值是否一样,可能会跟自己的实际需求不一样

List

特点:添加的元素 有序、可重复、有索引

有序:存和取的顺序一样

可重复:集合中存储的元素可以重复

有索引:可通过索引获取每个元素

ArrayList
LinkedList
Vector
Set

特点:添加的元素 无序、不重复、无索引

无序:存和取的顺序不一样

不重复:集合中存储的元素不可以重复

无索引:不可通过索引获取每个元素

HashSet

无序、不重复、无索引

LinkedHashSet

有序、不重复、无索引

TreeSet

可排序、不重复、无索引

哈希值
  • 根据 hashCode 方法算出来的 int 类型的整数

  • 该方法定义在 Object 类中,所有对象都可以调用,默认使用地址值进行计算

  • 一般情况下,会重写 hashCode 方法,利用对象内部的属性值计算哈希值

对象的哈希值特点
  • 如果没有重写 hashCode 方法,不同对象计算出的哈希值是不同的

  • 如果没有重写 hashCode 方法,不同的对象只要属性值相同,哈希值也是相同的

  • 在小部分情况下,不同属性值或不同地址值计算出来的哈希值也有可能一样(哈希碰撞)

双列集合

Collection 遍历方式

迭代器遍历

迭代器在 Java 中的类是 Iterator,迭代器是集合专用的遍历方式

Collection 集合获取迭代器

方法名称								说明
    
Iterator<E> iterator()					返回迭代器对象,默认指向当前集合的0索引

Iterator 类中常用的方法

方法名称								说明
    
boolean hasNext()						判断当前位置是否有元素,有就返回 true,没有就返回 false
    
E next()								获取当前位置的元素,并将迭代器对象移向下一个位置
    
void remove()							删除当前迭代器指向的元素 
Iterator<String> it = list.iterator();//创建一个叫 it 的指针

boolean flag = it.hasNext();//判断list集合中it指针当前位置是否有元素

String str = it.next();//获取当前指针指向的元素,并向后移动指针一个位置

while(it.hasNext()){//循环整个集合,获取每个元素
    
    String str = it.next();
    
    sout(str);
    
}

注意

  • 报错:NoSuchElementException(没有这个元素)

  • 迭代器遍历完毕,指针不会复位

  • 循环中只能用一次 next 方法

  • 迭代器遍历时,不能用集合的方法进行增加或删除

  • 如果想继续第二次遍历,只能再次获取一个新的迭代器对象

  • 不能用集合的方法进行增加或删除,只能用迭代器的删除方法来删除,添加暂时没有办法

增强 for 遍历

  • 增强 for 的底层就是迭代器,为了简化迭代器的代码书写的

  • 是 JDK5 之后出现的,其内部原理就是一个 Iterator 迭代器

  • 所有的单列集合和数组才能用增强 for 进行遍历

格式

for(元素的数据类型 变量名:数组或集合){

​ …;

}

for (String s : list){
    sout(s);
}

注意

  • s 就是第三方变量,在循环过程中依次表示集合中每一个元素

  • 修改增强 for 中的变量,不会改变集合中原来的数据

快速生成方式:集合名字 + for + 回车

Lambda 表达式遍历

从 JDK8 开始的新技术,提供了一种更简单、直接的遍历集合方式

方法名称																说明
    
default void forEach(Consumer<? super T> action):					结合Lambda遍历集合
Collection<String> coll = new ArrayList<>();

coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");

//匿名内部类:

coll.forEach(new Consumer<String>(){
    @Override
    //s表示集合中每一个数据
    public void accept(String s){
        sout(s);
    }
});

//Lambda表达式:

coll.forEach(s -> sout(s));

List常用方法和五种遍历方式

  • Collection 的方法 List 都继承了

  • List 因为有索引,所以多个很多索引操作的方法

方法名称										说明
    
void add(int index,E element)					在集合指定索引插入指定元素,其余向后移一位
    
E remove(int index)								删除指定索引的元素,返回被删除的元素
    
E set(int index,E element)						修改指定索引的元素,返回修改前的元素
    
E get(int index)								返回指定索引的元素

调用方法时,如果方法出现重载,会优先调用形参和实参一样的方法

如: remove()有两个重载,一个形参是index,一个是String

如果形参是数字,那参数就是代表集合中的索引

手动装箱

Integer i = Integer.valueOf(1);

list.remove(i);	//此时 i 就是元素 1,而不是索引 1

遍历方式

迭代器

Iterator<String> it = list.iterator();
while(it.hasNext()){
    String str = it.next();
    sout(str);
}

增强 for

for (String s : list){
    sout(s);
}

Lambda 表达式

list.forEach(s -> sout(s));

普通 for 循环

for(itn i = 0; i < list.size(); i++){
    String s = list.get(i);
    sout(s);
}

列表迭代器

方法名称											说明
    
boolean hasNext()						判断当前位置是否有元素,有就返回 true,没有就返回 false
    
E next()								获取当前位置的元素,并将迭代器对象移向下一个位置
    
void remove()							删除当前迭代器指向的元素 
    
E previous()							获取列表中前一个元素
    
boolean hasPrevious()					判断前面是否有多个元素,有就返回 true,没有就返回 false

五种遍历方式对比

迭代器遍历							在遍历过程中需要删除元素,用迭代器
    
列表迭代器							在遍历过程中需要添加元素,用列表迭代器
    
增强for遍历和Lambda 					仅仅想遍历,用增强forLambda
    
普通for							  如果遍历时想操作索引,用普通for

HashSet

HashSet JDK8 以前的底层原理

  • 创建一个默认长度16,默认加载因为0.75的数组,名为 table

  • 根据元素的哈希值跟数组长度计算出应存入的位置

​ int index = (数组长度 - 1) & 哈希值;

  • 判断当前位置是否为 null ,是则直接存入

  • 如果不是 null,则调用 equals 方法比较属性值

  • 一样:不存

  • 不一样:存入数组,形成链表

​ JDK8以前:新元素存入数组,老元素挂在新元素下面

​ JDK8以后:新元素直接挂在老元素下面

JDK8以后,当链表长度超过8,而且数组长度 >= 64时,自动转换为红黑树

如果集合中存储的是自定义对象,必须要重写 hashCode 和 equals 方法

问题

为什么 HashSet 存和取的顺序不一样

HashSet通过地址值去计算得到的哈希值就是我们应该存放的位置。

因此,当我们在程序中添加元素时,虽然有些元素添加是在前面,但是它计算出来的地址值是不一样的

有的位置在前,有的位置在后,所以造成了取出来的顺序就不一样。

为什么 HashSet 没有索引

首先,我们讲讲为什么HashSet没有重复元素,前面我们说过了,数据存入HashSet中是先根据地址去计算哈希值然后根据哈希值存入数组,那么如果添加相同元素,这个时候,你要存入数组中,这个位置已经有一个元素了,那么会调用equals方法去比较一下,如果相同,那么就不存入,保留无重复的特性,不同就存入(见下方说明)。

第二,我们在数据结构与算法课程中了解到,存入元素是会有哈希冲突的,什么意思呢?意思是,可能会有其它元素计算出来的哈希值跟之前存入的数据所存入到数组中的下角标冲突了,那么java是如何解决的呢?JDK8之前,是采用了数据+链表的方式去实现,对于哈希值冲突的元素,新元素会存到数组中,那么原来的那些元素就会与新元素形成一个链表。JDK8以后,采用数组+链表+红黑树的方式去解决,原来添加的元素放在数组中的位置是不变的,新元素在下方形成一个红黑树。

所以通过以上了解,我们就清楚为什么没有索引了,比如,假设如果有索引的话,在索引为1的下角标有很多元素,那么我们就不清楚到底取哪个元素了,这就是为什么没有索引的原因。

HashSet 利用什么保证数据去重的

在底层运用equals和hashCode俩个方法保证去重唯一性

在问题二中我们讲到过,对于相同元素,比较如果相同,那么就不存入

hashCode主要用来去计算出我们元素所应该存入的位置。

那么对于自定义的类型数据,重写(记住要在自定义类型里面重写,而不是测试类里面)过后的hashCode会根据对象的内部属性值去计算我们的哈希值,对于内部属性值一样的计算出来的哈希值就是一样的,然后再对应的去调用equals比较是否相同,看是否存入(相同不存,反之)。

LinkedHashSet

  • 有序

  • 不重复

  • 无索引

保证存储和取出的元素顺序一致

底层原理

底层数据结构依然是哈希表,只是每个元素额外多了一个双链表的机制记录存储的顺序

TreeSet

  • 不重复

  • 无索引

  • 可排序

按照元素的默认规则(有小有大)排序

底层原理

基于红黑树的数据结构来实现排序的,增删改查性能都比较好

默认规则

  • 对于数值型:Integer、Double 默认按照从小到大的顺序进行排序

  • 对于字符、字符串类型:按照字符在 ASCII 码表中数字升序进行排序

算法

排序

冒泡排序

public static void BubbleSort(int[] arr){
    for(int i=0;i < arr.length-1;i++){
        for(int j=0;j < arr.length-1-i;j++){
            if(arr[j] > arr[j+1]){
                int temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

选择排序

public static void SelectionSort(int[] arr){
    for(int i=0;i < arr.length-1;i++){
        for(int j=i+1;j < arr.length;j++){
            if(arr[i] > arr[j]){
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

快速排序

public static void QuickSort(int[] arr,int i,int j{
    
    int start = i;
    int end = j;
    
    int baseNumber = arr[i];
    
    while(start != end){
        while(true){
            if(end <= start || arr[end] < baseNumber){
                break;
            }
        }
    }
    
}

插入排序

public static void InsertSort(int[] arr){
    int startIndex = -1;
    for(int i=0;i<arr.length-1;i++){
        if(arr[i] > arr[i+1]){
            startIndex = i+1;
            break;
        }
    }
    
    for(int i=startIndex;i<arr.length;i++){
        int j = i;
        while(j > 0 && arr[j] < arr[j-1]){
            int temp = arr[j-1];
            arr[j-1] = arr[j];
            arr[j] = temp;
            j--;
        }
    }
    
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值