java初学(仅供自己复习)

目录

一、数据类型

1.基本数据类型

2.引用数据类型(除基本类型之外的其他)

二.运算符

1.隐式转换

2. 字符串的+操作       和    字符的+操作

3.扩展赋值运算符

三. 数组

1.数组的静态初始化

 2.数组的动态初始化

3.两种初始化之间的区别

 4.数组的length属性

5.产生一个随机数

6.利用随机数 打乱一个数组

7.将一个数组的元素 变为 倒序

8.我所出现的错误:

四.  方法

1.固定格式

 2.方法的重载

3.引用数据类型和基本数据类型(和JavaScript有点像)

五. 面向对象基础

1.this关键字

 2.构造方法

3.标准javabean

4.面向对象出现的错误

5.vscode快捷键   

    (1)代码格式化shift +alt +f   

    (2)迅速生成setter/getter方法 

6.java里也有类似c语言输出的格式化函数

​编辑

7. 键盘录入

8.对象成员变量默认值的规则

 9.对象的内存图

 (1)一个对象的内存图

(2)两个对象的内存图

六. 字符串

1.不需要导包

2.特点

3.String 类的 构造方法

4.内存分析

5.字符串的比较

6.拼接字符串(有所收获)

8.StringBuilder

(1)提高效率

(2)链式编程

(3) StringBuilder的应用场景

(4)StringBuilder常见方法

(5)构造方法

9.StringJoiner

1.介绍

​编辑

2.构造方法

10.字符串相关底层原理

(3)面试水题

(4)小总结

11.字符串常见方法

 12.课后练习的收获

(1)​编辑

(2)将数字转为字符串(特别简单)

七. 集合

1.集合介绍

2.往集合 里 添加内容的 小坑

八.  学生管理系统

1.学到以及回顾的知识

(1)跳出循环

(2)String的startWith()方法

(3)封装思想的应用

2.不同函数 内容重复 后的改善

3.改善性能的一些做法

4.出现的错误

九.面向对象进阶

1.static

1.1static 概述

1.2 工具类

 1.3 static注意事项

1.4 static原理分析

2.用类创建对象后,成员变量具有默认值

3.类的继承

3.1类的继承概述

3.2继承的特点

3.3 子类能继承父类的那些内容

3.4 继承类中访问成员变量和成员方法的访问特点

 3.5 方法的重写

3.6继承中的构造方法和this super关键字

十.类的多态

1.认识多态

2.多态调用成员的特点

 3.多态的优势和弊端

3.1 优势

 3.2多态的弊端

十一. 包和final

1.包

 2.final

 (1) final 修饰 方法

(2)final 修饰 类

 (3)final修饰变量

十二.权限修饰符和代码块

1.类的权限修饰符

2.权限修饰符

3.代码块

 3.总结

十三. 抽象类和抽象方法(跟继承有关)

十四、接口(跟抽象方法有联系)

1.概述

2.定义格式

 

3.接口中成员的特点

4.多学一招,jdk8,9后的新特性

①默认方法

 ②接口中定义静态方法

 ③总结​编辑

形参和返回值

十五.匿名内部类

十六.拼图游戏

十七.常用api接口

1.Math类

1.1常用方法

 1.2Math源码

2. System类

2.1常用方法


一、数据类型

1.基本数据类型

对我而言,比较新奇的是   long a=9999L;     要加L

                                           float b=88.8F;     要加F

2.引用数据类型(除基本类型之外的其他)

二.运算符

1.隐式转换

2. 字符串的+操作       和    字符的+操作

3.扩展赋值运算符

      /=   +=   -=  *=  %=

 4.逻辑运算符

 |   ^  &           ||      &&

                                                        ^是异或

       || 短路逻辑运算符,当有一个为true不用再检测另一个是否为true,但对于 |  无论怎样都需要检测所以的结果.  && 同理.

4.三元运算符

三. 数组

1.数组的静态初始化

 2.数组的动态初始化

3.两种初始化之间的区别

 4.数组的length属性

利用 length 属性可获取数组的长度

5.产生一个随机数

第一步,导入java.util.Random; 包

第二步,初始化Random 变量名 = new Random();

第三步,赋值给一个变量,可以划定范围

import java.util.Random;
public class RamdomDemo{
public static void main(String[]args){
Random r=new Random();
//number 的范围是一个0~100的整数
int number=r.nextInt(100);
//可通过某些手段来,让范围改变,不包括0
int magic=r.nextInt(99)+1;
//此时范围为1~100
}
}

原来可以直接用origin来划定起始范围,我原来是不知道的。起始数也可以是 负数。

6.利用随机数 打乱一个数组

7.将一个数组的元素 变为 倒序

8.我所出现的错误:

(1)      

 第一个方括号里面不应该添加内容

四.  方法

1.固定格式

 2.方法的重载

方法的重载  只需看参数和方法名 , 跟返回值无关.(跟c++的重载很像)

3.引用数据类型和基本数据类型(和JavaScript有点像)

五. 面向对象基础

1.this关键字

跟JavaScript的作用域链很像.

 2.构造方法

3.标准javabean

可用快捷键迅速生成 .

4.面向对象出现的错误

blood 属性已经被 private 修饰了,因此不能通过直接调用来修改它,需要用  方法 来获取或改变它.

5.vscode快捷键   

    (1)代码格式化shift +alt +f   

    (2)迅速生成setter/getter方法 

如下图, 点击Source Action ,里面有  setter 和getter方法.

6.java里也有类似c语言输出的格式化函数

第一个参数是字符串, 里面可以含有%s,%d   ,后面的参数即为%s和%d的内容

7. 键盘录入

两种体系不能混用,不然会出现在c语言里那种,把回车符当作字符录入.

8.对象成员变量默认值的规则

 9.对象的内存图

 (1)一个对象的内存图

study()方法 执行完毕后出栈,此时main()方法也执行完毕,也要出栈.

于是,main()方法里的 变量 随之消失,

没有变量指着堆内存的那块空间,于是那块空间就会被回收

 那块空间被回收了

(2)两个对象的内存图

class字节码文件加载一次后,再用这个类就不需要再加载一次了.

六. 字符串

1.不需要导包

java.lang包是java语言的核心,它提供了java中的基础类。包括基本Object类、Class类、String类、基本类型的包装类、基本的数学类等等最基本的类。

2.特点

  • 字符串不可变,它们的值在创建后不能被更改

  • 虽然 String 的值是不可变的,但是它们可以被共享

  • 字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )

例子:该图中代码 创造了 两个字符串 ,虽然输出m后结果为 "123",但"12"并没有消失.

3.String 类的 构造方法

 (1)public String()  创造空字符串 和 public String(String original) 根据传入的字符串创造字符串很少用,

 (2)public String(char[ ] chs)     可以通过 字符数组来改变字符串 的值

 (3)byte里面放的是整数,当传入byte[ ] 后,会根据 数字对应的字符 进行创建

4.内存分析

(1)当使用直接赋值的方式时,会用到  StringTable,去寻找 String Table 里面找是否存在这样的字符串.

存在返回其String Table 对应的地址

不存在 创建该字符串 并返回其 地址.     (节约内存)

 

5.字符串的比较

6.拼接字符串(有所收获)

(1)原来在求回文数时,要用到  newNum+=singleDigit+sum*10;来让数字倒过来.

int number=123456;
int newNum=0;
while(number!=0){
//求个位数,从右到左
int singleDigit=number%10;
number/=10;
newNum+=singleDigit+sum*10;

}

//循环后,newNum的值 最先求得的个位数为最高位,依次递减.
//即sum为回文数,值为 
sout(newNum);

在字符串拼接时,可通过调换字符串的位置来改变,这点真的很神奇.

7.几个常见的字符串方法

8.StringBuilder

(1)提高效率

当进行字符串拼接时,由于拼接一次,便创造一个新的字符串,很浪费空间.

这时,可以用SringBuilder进行创建,拼接时内容是可变的,不会产生新的.

 

(2)链式编程

(3) StringBuilder的应用场景

     字符串的拼接

     字符串的反转

(4)StringBuilder常见方法

(5)构造方法

有参构造
StringBuilder sb=new StringBuilder("abc");
无参构造
StringBuilder sb=new StringBUilder();

(6)StringBuilder补充

①StringBuilder除了可以使用 append ()方法  向后追加字符串,

它也可以通过 Insert方法向 前追加字符串

 //十进制转化为字符串二进制
        int number = 10;
        StringBuilder sb= new StringBuilder();

        while (number != 0) {
            //取余数
            int remainder = number % 2;
            //求商
            number = number / 2;
            //拼接在前面,即一直插在为0的位置
            binaryStr.insert(0, remainder);

        }
        
        String binaryStr=sb.toString();
        System.out.println(binaryStr);

9.StringJoiner

1.介绍

2.构造方法

两种构造方法
StringJoiner sj=new StringJoiner(间隔符);
StringJoiner sj=new StringJoiner(间隔符,前缀,后缀);




sj.add();      //往sj里添加一个元素
sj.length();   //返回sj的长度
sj.toString(); //将StringJoiner类型转换为String类型

10.字符串相关底层原理

(1)拼接时无变量参与

(2)拼接时有变量参与,jdk8之前,

变量与字符串相加,拼接一次会创建两个对象,先创建StringBuilder(),再用toString()方法将其转换为字符串.  即 StringBuilder()创建了一个对象,转为字符串又创建一个对象.

 

jdk8后:如下图,会提前做一个预估,然后把字符串放在一个数组里面,然后变作字符串.

因此,对于 红线 所划的代码,预估s1 ,s2 ,s3的长度,把它们放进数组里面.

 但在下图,变量与字符串多次相加,需要预估多次,这其实也是比较浪费时间的.

(3)面试水题

(4)小总结

11.字符串常见方法

 12.课后练习的收获

(1)

     调用charAt()方法遍历来获取的 子串,也可以用subString()方法来提取字符串

     调用String.toCharArray()方法,使 字符串 变为 字符数组,对字符数组里的元素进行改变.

(2)将数字转为字符串(特别简单)

就让   空字符串和数字     相加即可.

七. 集合

1.集合介绍

 

ArrayList.indexOf(object o);       返回集合中 对象o 的索引值

对于移除元素的方法ArrayList.remove()具有重载函数

 不能用基本数据类型,但可以用基本数据的包装类

2.往集合 里 添加内容的 小坑

下图将 学生对象s的地址添加到集合中,所以同一个地址里的内容相同.

补充:

集合里面的不同索引下的内容可以重复;????试了试Chracter的包装类,应该是可以的。

八.  学生管理系统

1.学到以及回顾的知识

(1)跳出循环

变量名:         break 变量名; 

如下图的loop1

 可以从一个地方跳到另一个地方,但要 谨慎的使用.

System.exit(0)

(2)String的startWith()方法

(3)ArrayList 集合

ArrayList <>里面 不可以使用基本数据类型,必须使用引用数据类型.

但是,你可以用 用ArrayList(Character),用Character这个包装类来当作 char来使用.

同理,你可以用其他 基本数据类型的包装类 来实现

泛型的定义主要有以下两种:

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)

  2. 在程序编码中一些包含参数的。其参数可以代表类或对象等等。(现在人们大多把这称作模板

不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。

(3)封装思想的应用

2.不同函数 内容重复 后的改善

3.改善性能的一些做法

(1)下图中,需要调用两次 String的length方法,可以进行改善,将 长度 提前 储存在一个变量

 改善后:

4.出现的错误

(1)

我把 lassLetter的判断写错了,lastLetter应该 是 '0'=<lastLetter<='9'

我把其中的 &&错写为||

if(lastLetter=='x'||lastLetter=='X'||lastLetter>'0'||lastLetter<'9'){return true;}

(2)StringBuilder创建的对象后,加  字符  ,会出现奇怪的事情,最后的结果很好笑

探究如下:

九.面向对象进阶

1.static

1.1static 概述

自说自述:当用static 修饰变量和方法时,

①  类名.变量   或 类名.方法() 直接调用,

② 通过 创建一个对象, 再通过对象来调用

但对于 一般的成员变量和方法 (无static修饰),只能创建对象后,利用 对象来调用

1.2 工具类

(1) 私有化构造方法,    工具类就是个工具, 不能够创建它的对象,所以要把它的 构造方法 私有化,这样,就不会误操作.

(2)方法定义为静态,即static , 这样可以直接用 类名.方法名() 来调用.

 

 1.3 static注意事项

(1) 静态方法只能访问 静态变量和静态方法.  

否则编译器会给你报错

 (2)非静态方法可以访问 静态变量,静态方法,

 (3)静态方法没有this关键字

对于一般的方法,每创建一个对象,对象拥有属于它自己的成员变量 ,因此有多个对象,this关键字就能确定它是谁,通过调用方法而去修改它.

但对于static定义的静态方法,这是共享的方法,每个创建的对象只共享这一个方法,因此它是独一无二的,没必要取用  this关键字 来区分,只需用类名,方法() 调用就行.

1.4 static原理分析

2.用类创建对象后,成员变量具有默认值

当未对数组初始化时,数组中的元素也会有默认值.

3.类的继承

3.1类的继承概述

 

3.2继承的特点

(1) 我们来详细分析一下第3条

一个类可以继承一个直接父类,

而一个类的父类也可以继承一个父类,

一个类的父类的父类的父类也可以继承一个父类

................................................................................,难道无穷无尽??????

其实追溯到源头,最牛逼的 是 Object类 ,它是所有类的 父亲,或是父亲的父亲

当我们随便写一个类,而且这个类里面什么也不写,但你却可以调用 Object类的方法

AMAZING!

3.3 子类能继承父类的那些内容

(1)先研究  父类的 构造方法和成员变量   是否能被继承

(2) 父类的 成员方法 能否被继承

 ①对于父类的成员方法, 只有  不被  private static final 修饰的成员方法才能被继承

 ②虚方法表可以提高查找 效率

 ③satic是否也是继承???尚未搞清final好像也能继承,但不能重写??啥情况???

3.4 继承类中访问成员变量和成员方法的访问特点

(1)成员变量的访问特点

 (2)成员方法的访问特点

① 继承的类不是有  虚方法表吗,为什么还会有就近原则??????

因为

有 方法重写,虽然有虚方法表,但是当发生 方法重写时,会覆盖 父类的成员方法,因此子类会先找的新的虚方法表中的方法.

②如果想调用父类的方法怎么办?????

可以用   super.父类方法()  来调用父类的方法

基于②的 例子如下图,先利用super关键字调用父类的eat()

 3.5 方法的重写

补充:重写时不按照规则时,

例1:父类与子类的方法名一样,但参数列表不一样。

这时就不是重写了,不会覆盖 父类的方法,而是重载,既能调用父类的方法也能调用子类的方法。

 分析第5条,只有被添加到虚方法表中的方法才能重写.

     我把一个父类的用final修饰的成员方法在子类里进行重写,发现报错了,

     因为final修饰的成员方法没有添加到虚方法表中,所以无法进行重写.

3.6继承中的构造方法this super关键字

①构造方法不会被继承,但可以用super()来执行父类的构造方法

当在 本类的构造方法 调用  本类的其他构造方法时,必须把 调用的构造方法 写在第一行.

此时虚拟机不会在第一行添加super();

这是为啥?

因为 本类的其他构造方法的第一行会默认有个super(),调用该构造方法也就代表:调用了其 第一行默认的super

 

 可以通过 无参构造里面 调用其它 有参构造方法

来在创造对象(new 类名()不 需要加参数)时得到默认值.例如下图

 有一个疑问???

为什么通过调用super()的含参构造方法就可以让本类的成员变量被赋值,

因为super()的含参构造里的this关键字所指向 调用它的对象. 像下面就可以构建全参的构造方法.

十.类的多态

1.认识多态

 

2.多态调用成员的特点

 下面这个图很重要,  调用成员方法 编译看左边,运行看右边.

 

我们知道对于多态时 调用成员方法,编译看左边,运行看右边.

但对于下面这个例子,Husky类  继承于 Dog类

①使用多态,运行时调用子类的方法 , 但此时如果子类没有重写该方法, 就会去调用父类的该方法,

就像下图的例子,如果Husky子类没有eat()方法,就会去找它的父类的eat()方法.

为什么可以找父类的eat()方法?

因为在编译时就会查看父类是否有所要调用的eat()方法,

 

 

 3.多态的优势和弊端

3.1 优势

 像下面的add中的形参为Object ,由于每一个类都直接或间接继承于Object,

因此这就形成了一种多态,Object e= new  子类( );

可以将任何对象加入 ArrayList

 

 3.2多态的弊端

 

 问题: Dog类 继承于 Animal类   

          Animal a=new Dog();

 但我却想调用Dog类的  特有的方法  ,好像是无法实现的???

但通过强制类型转换可以解决这个问题,

要知道父类是比子类大的,因此进行强制类型转换

像下面的Dog d=a;

报错的原因是 a是Animal引用类型 ,d是Dog引用类型,a比d大,不可以把一个大的赋值给一个小的

,除非使用强制类型转换

即为Dog d= (Dog) a;

 

上面虽然解决了关于多态  调用子类特殊方法的问题,

但 我们很难去 分清楚   所要强制转换的类型 是谁?

即对于 Dog d=(Dog) a     我们Animal类转换为了Dog类型

但我们如果错写为 Cat c=(Cat) a  ,就会发生调用错误

原因如下: 因为Animal a=new Dog();  右边是Dog类,可以用强转将a转换为Dog类,但却不能转换成其它类

 

 为了解决强转时,出现转换错误的情形可以用 instanceof,

在jdk14引入了一个新特性,可以使 instanceof  更加方便

/*
多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
*/

//Animal 是 Dog的父类
Animal a=new Dog();//多态
//

if(a instanceof Dog  d);  //判断 a是不是 Dog的实例

//相当于
if(a instanceof Dog)
  {
    Dog d= (Dog)a
  } ;

十一. 包和final

1.包

 

 

 2.final

 (1) final 修饰 方法

表示该方法不能在子类中被重写

(2)final 修饰 类

则该类是最终类,不能被继承

 

 (3)final修饰变量

①则该变量是一个常量,它所储存的 数据值 不可改变

②当变量是引用类型时,其存储的是地址值,地址值不可改变,  但是  这个地址值里的内容(成员变量,方法)可以改变

十二.权限修饰符和代码块

1.类的权限修饰符

2.权限修饰符

3.代码块

静态代码块只执行一次!!!

像下图,可以将static ArrayList<User>..........不加static,写进main()方法中,

效果看起来是一样的,但实际上真的一样吗?

但是在 其它类 反复调用main()方法时,此时,main()方法里的 ArrayList<User>.........,以及list.add()会被反复执行.

但你用static 代码块便可以随着类的加载只执行一次 static代码块里面的内容,当多次调用main()方法时,不会让这个初始化过程重复

 

静态代码块里面只能初始化 static修饰的成员变量。

 3.总结

十三. 抽象类和抽象方法(跟继承有关)

1.由于 不能确定父类方法中要写什么,所以只写函数的声明就行,记得加 分号

类需要变作 抽象类

 

 

为什么 抽象类 不能创建对象,但还要写 它的构造方法????

因为: 这是为它的子类着想,子类可通过 super() 来 写它的构造方法。

 2.抽象方法可以强制让 重写的函数按照一种格式书写

第一个图是父类的eat()方法,第二个图是子类的eat()方法

十四、接口(跟抽象方法有联系)

1.概述

//接口像抽象类一样不能创建对象

2.定义格式

 

3.接口中成员的特点

4.多学一招,jdk8,9后的新特性

①默认方法

前情回顾:当类实现一个接口时,需要重写接口中的所有抽象方法,

                  但是当我们不想重写其中的某个方法时应该怎么办?

                  就可以将接口中的方法 用 default修饰

实操:

(1)定义一个接口,里面有 一个默认方法  一个抽象方法

 (2)在 实现类 里可以   不用    重写默认方法(default修饰) ,

但你想重写默认方法时,在实现类中 重写的默认方法 就不要加 default修饰了

 ②接口中定义静态方法

 

不加static的私有方法,为默认方法服务。

    加static的私有方法,为静态方法服务。

 

 ③总结

形参和返回值

 

十五.匿名内部类(不太熟)

其实叫做匿名内部类的对象 更合适,可以后跟 .方法名() ;  来调用方法

 

十六.拼图游戏

1.crazy的匿名内部类

 很明显,addActionListener需要 一个 ActionListener 类型的参数

而ActionListener是一个接口

 因此,我们  要用创建  一个实现ActionListener接口的类 ,并用其 对象作为参数。

如下,当点击 jButton 后,就会执行所传参数mAL中的 actionPerformed()方法,进而实现点击后 所要发生的事。

 

事情到此感觉较为完美了,但对于按钮,很多都是不一样的,去创建一个实现接口的类,再创建对象进行传参,有些麻烦。

匿名内部类可以解决这个问题,准确来说是 匿名内部类的对象,如果忘记匿名内部类是什么,回去复习!!!

还有一种方法:

import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyFrame extends JFrame implements ActionListener {
    // 将Button设为成员变量
    JButton jButton1 = new JButton("jButton1");
    JButton jButton2 = new JButton("jButton2");

    public MyFrame() {
        //创建界面 的对象
        JFrame jFrame = new JFrame();
        //设置界面大小
        jFrame.setSize(600, 600);
        //使该界面始终在其他应用程序的界面之上
        jFrame.setAlwaysOnTop(true);
        //将界面放在屏幕中间
        jFrame.setLocationRelativeTo(null);
        //设置默认关闭模式
        jFrame.setDefaultCloseOperation(3);
        // 如果不设置Layout那么就显示不出来了
        jFrame.setLayout(null);
        // jButton.setSize(100, 100);可用setSize设置宽和高

        // 设置按钮坐标和宽和高
        jButton1.setBounds(100, 100, 100, 100);
        jButton2.setBounds(0, 0, 100, 100);
        // 给按钮添加ActionListener事件,
        //this为本类的对象,由于本类为实现了 ActionListener接口的实现类 ,
        //因此可将实现类的对象(this指向对象的位置)作为 jButton1.addActionListener(ActionListener l) 方法的  参数
        jButton1.addActionListener(this);
        jButton2.addActionListener(this);
        jFrame.getContentPane().add(jButton1);
        jFrame.getContentPane().add(jButton2);
        jFrame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("你好");
        Object source = e.getSource();
        if (e.getSource() == jButton1) {
            jButton1.setSize(200, 200);
            System.out.println("jButton1被调用");

        } else if (source == jButton2) {
            System.out.println("jButton2被调用");
        } else {
            System.out.println("按钮未被点击");
        }
    }
}

十七.常用api接口

1.Math类

1.1常用方法

     Math.sqrt() 返回平方根     //sqaure root

     Math.cbrt() 返回立方根     //cube root

question1:

abs可能会产生 一些与溢出相应的bug,像下图,我明明想要正数,它却给我一个负数,这是因为

int型的变量的 最小值的绝对值 大于 最大值,因此 使用 Math.abs()时 没有对应的正数,所以打印的是一个 负值。

可以用 Math.absExact(), 当 负数无对应正数值后 抛出错误。

具体看下节   1.2Math源码 的内容。

 1.2Math源码

Math类 被final修饰符修饰,表示不能 被继承. 构造方法私有化,不能够创建其相应的对象。

即Math类是一个工具类。

//还记得 final修饰符的用法吗?

//回顾一下,final修饰类,方法,变量 时,发生的奇妙事件。 

②解决溢出不报错的问题

为什么 没有与float double相应的重载方法

好像是 因为 double和float的最大值 远远大于 最小值的绝对值。

 

 

当传进来两个参数,代表两个参数的和。(老眼昏花了,这是addExact)

 

2. System类

2.1常用方法

 ①  System.exit(0)            正常停止

      System.exit(非零值)    异常中止

currentTimeMillis()        返回系统距离 1970年1月 有多少毫秒,可用其计算  程序运行的时间。

下图是一个小用法

 ③ System.arraycopy(源数组,原数组索引,目标数组,目标索引,拷贝长度  )

注意 引用数据类型 的数组

 当元素是 基本数据类型 时

当类型是 引用数据类型 时,在下图中,可以给相同引用数据类型赋值

可以给父类数组赋值,如下图Person类是Student类的父类。

但这方面要联想起  多态 ,什么时候是大转小,什么时候是 小转大,什么时候能调用子类的特有方法。

3.Runtime类

3.1常用方法

代码实践:

 

3.1.1

Runtime类不能直接创建对象,需要用调用Runtime类的静态方法

看下图源码:Rumtime的方法被私有化,不能创建对象。需要利用 Runtime.getRuntime()

为什么要这样,Runtime表示运行环境,因为运行时虚拟机只能有一个,只能有一个运行环境,因此不能创建多个Runtime类的对象。


怎样改变final定义的常量,system.out中的out好像被改变了
因为out是引用数据类型,所以内容可以变,但指针的指向不能变

 

4.Object类

4.1构造方法

Object是所有类的祖先,由于它的子类不具有共性,因此它没有成员变量只有 空参构造方法。

每一个类构造方法的 第一行都有一个默认 的super()来调用父类的构造方法,这也是Object类只有空参构造的所导致的。

 4.2常见方法

 要想 浅 克隆一个类的对象,

①首先要看 将要克隆的这个类 是否实现了Cloneable 接口

这个Cloneable接口很有意思,是一个标志性接口,内部没有任何抽象方法,只是作为一个标记。

②由于Object类的 clone()方法由 protected权限修饰符修饰,因此只能在它的子类中成员方法或main方法内使用此方法,但在其他 类 不能够使用此方法。

因此为了 在其它类中 能够使用Object的clone方法,就需要在子类中重写该方法。

如下图,通过 super关键字 调用了父类的clone()方法。

//  父类,也就是Object类的clone方法是  浅克隆

 ③深克隆应该怎么写

 如面的代码,

我们自己再创建一个数组就可以了,字符串会放在 串池中 进行复用,而其他引用类型会创建新的

//Cloneable
//如果一个接口里面没有抽象方法
//表示当前的接口是一个标记性接口
//因此,Cloneable是一个标记性接口,他仅仅起到标记的作用,内部没有任何代码
//现在Cloneable表示一旦实现了,那么当前类的对象就可以被克降
//如果没有实现,当前类的对象就不能克隆
public class User implements Cloneable {
    private int id;
    private String username;
    private String password;
    private String path;
    private int[] data;


 @Override
    protected Object clone() throws CloneNotSupportedException {
        
        //先把被克隆对象中的数组获取出来
        int[] data = this.data;
        //创建新的数组
        int[] newData = new int[data.length];
        //拷贝数组中的数据
        for (int i = 0; i < data.length; i++) {
            newData[i] = data[i];
        }
        //调用父类中的方法克隆对象
        User u = (User) super.clone();
        //因为父类中的克隆方法是浅克隆,替换克隆出来对象中的数组地址值
        u.data = newData;
        return u;
    }

}

④但在实际开发中,我们往往引入第三方工具

4..3重写父类 Object 的 toString()方法,equals()方法

顺便回顾一下多态

① toString返回值是 包名+类名+地址值,感觉Object类的这个toString方法并没有什么用,

所以可以对toString()进行重写

 ②Student类中重写 equals方法,用到了Objects(呲)的equals方法。

下图为Objects 中的静态方法equals(Object a,Object b)

好像,他怎么没有进行全转

我自己加了些注释,如下面代码

@Override
    public boolean equals(Object o) {
        //判断是不是同一个对象,即地址值是否相同
        if (this == o) {
            return true;
        }
        //判断是否为同一个包下的同一种类
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        //由于o是object类型的,接收它的子类时,相当于多态。
        //编译看左面,运行看右边.
          当父类没有此方法时,会报错。有此方法时,会运行子类的该方法。
        //要想调用子类的 特有 成员方法和成员变量,就必须进行强转
        //注意!!!当想调用 子类的成员变量 ,也得强转


        //Student1 student1 = (Student1) o;
        Student1 student1 = (Student1) o;
        //判断字符串是否相等时,需要用到字符串的equals方法.
        //但在这里,调用了Objects.equals(Object a, Object b)方法
        //这个函数调用了字符串的equals方法,但是在调用前 判断了 对象a是否为空
        return age == student1.age && Objects.equals(name, student1.name);
    }

 4.4底层源码

①Object类的toString()方法的源码

②Object类中的equals()方法

它只判断 地址值 是否相同

③String类中重写的 equals()方法

会通过instanceof 判断 ,anObject 是不是String类的对象,如果不是会报错

4.5一个面试题

Stirng类 和 StringBuilder类 会重写 toString()方法。

而且在底层时,会有一系列判断。

5.Objects 类

先下面一个例子,需要 判断 调用equals方法的s1对象 是否为空。有点麻烦,可以用Objects里的equals()方法

 

 isNull()    nonNull()方法

6.BigInteger

6.1

数字较大超出long的范围,可以用BigInteger

 

通过public BigInteger(String val) 构造方法  ,给BigInteger的变量赋值。

可给BigInteger 指定进制, 像下图  将二进制的1000,转为10进制,赋值给 bi3 ,所以bi3值为8

 

通过 BigInteger的静态方法来获取 BigInteger的对象

 BigInteger类型的变量 不能通过 + - * / 来进行直接计算,需要调用相应的方法。

将 BigInteger 类型的变量可以转为int类型,记得用变量来接收。

6.2  BigInteger底层原理

crazy!!!!

十八.正则表达式

1.正则表达式作用一:检验字符串是否满足规则

 1.1基本规则

①上面的正则表达式只能匹配单个字符,下面可以用数量词

②下面这个验证邮箱号码的方式,有一点很有趣,把重复 的部分用括号括起来,后面再加 次数

③ (?i)  忽略它后面的字母的大小写,要想忽略某一部分,可以多加一个括号

1.2小结

 

1.3 出现的错误:

①误写反斜杠 与 正斜杠

[\d&&[^0]]\d{0,9} 是正确的
[\d&&^0]\d{0,9}   是错误的,因为^0应该用中括号 括起来

2.正则表达式作用二:在一段文本中,查找满足要求的内容

2.1本地数据爬取

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexDemo6 {
    public static void main(String[] args) {
        /* 有如下文本,请按照要求爬取数据。
                Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,
                因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台
                要求:找出里面所有的JavaXX
         */

        String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
                "因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";


        //1.获取正则表达式的对象
        Pattern p = Pattern.compile("Java\\d{0,2}");
        //2.获取文本匹配器的对象
        //拿着m去读取str,找符合p规则的子串
        Matcher m = p.matcher(str);

        //3.利用循环获取
        while (m.find()) {
            String s = m.group();
            System.out.println(s);
        }


    }

    private static void method1(String str) {
        //Pattern:表示正则表达式
        //Matcher: 文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。
        //          在大串中去找符合匹配规则的子串。

        //获取正则表达式的对象
        Pattern p = Pattern.compile("Java\\d{0,2}");
        //获取文本匹配器的对象
        //m:文本匹配器的对象
        //str:大串
        //p:规则
        //m要在str中找符合p规则的小串
        Matcher m = p.matcher(str);

        //拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
        //如果没有,方法返回false
        //如果有,返回true。在底层记录子串的起始索引和结束索引+1
        // 0,4
        boolean b = m.find();

        //方法底层会根据find方法记录的索引进行字符串的截取
        // substring(起始索引,结束索引);包头不包尾
        // (0,4)但是不包含4索引
        // 会把截取的小串进行返回。
        String s1 = m.group();
        System.out.println(s1);


        //第二次在调用find的时候,会继续读取后面的内容
        //读取到第二个满足要求的子串,方法会继续返回true
        //并把第二个子串的起始索引和结束索引+1,进行记录
        b = m.find();

        //第二次调用group方法的时候,会根据find方法记录的索引再次截取子串
        String s2 = m.group();
        System.out.println(s2);
    }
}

2.2有条件的爬取数据

①?=      ?:   ?!  的区别

2.3贪婪爬去与非贪婪爬取

 自己实操:

 String s = "Java自从95年问世以来,abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa" +
                "经历了很多版木,目前企业中用的最多的是]ava8和]ava11,因为这两个是长期支持版木。" +
                "下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
        String regex = "ab{1,100}";//数量词后面什么都不加,就是默认的贪婪爬去
        String regex2 = "ab{1,100}?";//数量词后面加 ? 非贪婪爬去
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(s);
        System.out.println("数量词后面什么都不加,就是默认的贪婪爬去:" + m.find() + "  " + m.group());
        //------------------------------------------------------------
        Pattern p1 = Pattern.compile(regex2);
        Matcher m1 = p1.matcher(regex2);
        System.out.println("数量词后面加 ? 非贪婪爬去:" + m1.find() + "  " + m1.group());

2.4正则表达式在字符串方法中的使用

①String .replaceAll()  ,替换所有

 自己实操:

//要求:将海绵宝宝替换成其他动画里的人物
        String s = "我的名字是海绵宝宝,我的朋友是派大星,海绵宝宝喜欢抓水母";
        String regex = "海绵宝宝";
        String newStr = s.replaceAll(regex, "史迪奇");
        System.out.println(newStr);

②String.split(regex);

 String s = "我的名字是海绵宝宝 , 我的朋友是派大星 , 海绵宝宝喜欢抓水母";
        String regex = "海绵宝宝";
        String newStr = s.replaceAll(regex, "史迪奇");
        System.out.println(newStr);

        String regex2 = " *(,|,) *";
        //String regex2=" *[,,] *"; 与上面等价
        String[] arr = s.split(regex2);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i] + i);
        }

2.5分组捕获

 

// 补充:非捕获分组 不占用组号

①判断单个字符

括号 括起来的是一个分组,可以用 //组号  的形式,使用某个组中的 内容

!!!!:使用的是  内容  ,像下面图中 ,

(.)表示一个字符,这是一个组,

当所判断的字符串是 a123a ,(.)中的内容是 a,  使用 \\1  可以获得这个组中的内容 a

 ②判断多个字符,原理同上。

 ③要求字符串的开始部分为重复的某字符

注意,组号 是按 左括号的数量算的。

 ④

 

 

当然 你也可以 不写 ?:   ,但是 你要知道它是什么。

十九. 时间类

1.Date类

 //Date类
        Date d1 = new Date();
        System.out.println(d1);
        //时间原点
        Date d2 = new Date(100);
        System.out.println(d2);
        //Date.getTime()   把日期 转化成对应的毫秒值
        long interval = d1.getTime() - d2.getTime();
        System.out.println(interval / 1000 / 60 / 60 / 24 / 365);
        //设置 距离时间原点 的毫秒值
        d2.setTime(0);
        System.out.println(d2);

2.Instant类 好像 代替Date类了

3.

4.对于类DateFormatter();  传进去的参数是 ZonedDateTime类的对象

5.LocalDateTime类 有getMonth()方法 ,getMonthValue()  但没有 getDayOfWeekValue()

6.这两个代表的天数的相差值不一样,duration计算的是 相隔的天数,把年和月都转换为天数。

而Period 只看 今天是几号,看号一样不一样。

 // 本地日期时间对象。
        LocalDateTime today = LocalDateTime.now();
        System.out.println(today);

// 出生的日期时间对象
        LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
        System.out.println(birthDate);

        Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数
        System.out.println("相差的时间间隔对象:" + duration);

        System.out.println("============================================");
        System.out.println(duration.toDays());//两个时间差的天数
        System.out.println(duration.toHours());//两个时间差的小时数
        System.out.println(duration.toMinutes());//两个时间差的分钟数
        System.out.println(duration.toMillis());//两个时间差的毫秒数
        System.out.println(duration.toNanos());//两个时间差的纳秒数

 

  // 当前本地 年月日
        LocalDate today = LocalDate.now();
        System.out.println(today);

// 生日的 年月日
        LocalDate birthDate = LocalDate.of(2000, 1, 1);
        System.out.println(birthDate);

        Period period = Period.between(birthDate, today);//第二个参数减第一个参数

        System.out.println("相差的时间间隔对象:" + period);
        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println("相差天数:" + period.getDays());

        System.out.println(period.toTotalMonths());
    }

 

7.DateFormatter类 可以对LocalDate LocalDateTime LocalTime 进行格式化

2.关于时间类的练习题

2.1

isLeapYear这方法真好用。

 

2.2

二十. 包装类

1.概述

 2.Integer类

 ①Integer的关于int的构造方法和静态方法 过时了,后面有解释,自动装箱和拆箱。

②Integer中关于String的构造方法也过时了,

    可用静态方法Integer,vlueOf()或用Integer,parseInt(String)   来讲传进来的字符串转化为Int类型

除此之外,

                 还有其余的Byte,Short,Long,Float,Double,Character,Boolean包装类

                  同样也可以将字符串转换为相应的 byte,short,float,double,boolean基本类型

                   但是   字符包装类 没有此方法。

需要注意:

/*
            public static string tobinarystring(int i) 得到二进制
            public static string tooctalstring(int i) 得到八进制
            public static string toHexstring(int i) 得到十六进制
            public static int parseInt(string s) 将字符串类型的整数转成int类型的整数
 */

//1.把整数转成二进制,十六进制
String str1 = Integer.toBinaryString(100);
System.out.println(str1);//1100100

//2.把整数转成八进制
String str2 = Integer.toOctalString(100);
System.out.println(str2);//144

//3.把整数转成十六进制
String str3 = Integer.toHexString(100);
System.out.println(str3);//64

//4.将字符串类型的整数转成int类型的整数
//强类型语言:每种数据在java中都有各自的数据类型
//在计算的时候,如果不是同一种数据类型,是无法直接计算的。
int i = Integer.parseInt("123");
System.out.println(i);
System.out.println(i + 1);//124
//细节1:
//在类型转换的时候,括号中的参数只能是数字不能是其他,否则代码会报错
//细节2:
//8种包装类当中,除了Character都有对应的parseXxx的方法,进行类型转换
String str = "true";
boolean b = Boolean.parseBoolean(str);
System.out.println(b);

3.装箱与拆箱

4.自动装箱和自动拆箱

 5.字符串与基本类型之间的转换

5.1基本类型转为字符串类型

5.2字符串类型转化为基本类型

还是 利用的是 包装类的 静态方法 ,将字符串转换为其他基本类型

二十一. 查找

1.二分查找

前提是 数据 应该是 顺序 排放的

下面这段代码有问题,请检查一下!!!!!!!!!

 public static int  binarySearch(int []arr,int key){
        int low=0;
        int high=arr.length-1;
        while(low<=high){
            int mid=(low+high)/2;

            if(key>mid){
                low=mid+1;
            }else if(key<mid){high=mid-1;}
            else{
                return mid;
            }
        }
        return -1;
    }

答案如下:不能让key和mid直接比较,因为mid是一个索引值,并不是数组中的值。

 public static int  binarySearch(int []arr,int key){
        int low=0;
        int high=arr.length-1;
        while(low<=high){
            int mid=(low+high)/2;
            System.out.println("mid="+mid);
            if(key>arr[mid]){
                low=mid+1;
            }else if(key<arr[mid]){high=mid-1;}
            else{
                return mid;
            }
        }
        return -1;
    }

2.二分查找的改进(插值查找)

3.斐波那契查找

 

4.分块查找

除了手动分块,怎样分块是一个很难的事情!

1.定义块

public class Block {
    int startIndex;
    int endIndex;
    int max;
}

2.实现块查找 所要 调用 的方法

public static int blockSearch(int[] arr,Block[] block,int key){

          //搜索在块的哪个索引中
    int index=getIndex(block,key);
    //在该块中顺序查找
    for (int i = block[index].startIndex; i <=block[index].endIndex ; i++) {
        if (arr[i]==key) {
            return i;
        }
    }

return -1;

}
public static int getIndex(Block[]block,int key){
    for (int i = 0; i < block.length; i++) {
        if(key<=block[i].max) {
            return i;
        }
    }
    return -1;
}
  }

二十二.  排序

1.冒泡排序

public static void bubbleSort(int[] arr)
    {

//外循环:表示我要执行多少轮。 如果有n个数据,那么执行n - 1 轮
        for (int i = 0; i < arr.length-1; i++) {
            boolean flag=true;
//内循环:每一轮中我如何比较数据并找到当前的最大值
            //-1:为了防止索引越界
            //-i:提高效率,每一轮执行的次数应该比上一轮少一次。
            for (int j = 0; j < arr.length-i-1; j++) {
                if(arr[j]>arr[j+1])
                {
                    int t=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=t;
                    flag=false;
                }
            }
            if(flag){break;}
        }

2.选择排序

public static void selectSort(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 t= arr[i];
                   arr[i]=arr[j];
                   arr[j]=t;
                }
            }
        }
    }

3.插入排序

写这个的时候有点坎坷

1.下面是错误的

 public static void insertionSort(int []arr){
        //1.找到无序的哪一组数组是从哪个索引开始的。
        int startIndex=1;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>arr[i+1]){
           break;
            }
            startIndex++;
        }

        for (int i = startIndex; i < arr.length; i++) {
            for (int j = i-1; j>=0; j--) {
     
//-------------------------------------------------------从下面开始错了
//因为i值只有在内循环结束后才会交换,此时if语句中一直判断的是不变的i位置,交换位置也一直跟这个固定不变的i位置进行交换,不能达到我们想象中的效果。
           //如果startIndex大于有序的最后一个元素,不用对startIndex进行交换
     
                if(arr[i]>=arr[j]){
                    break;
                }
                //交换位置
                int t=arr[i];
                arr[i]=arr[j];
                arr[j]=t;
            }
        }

    }

2.下面是正确的

public static void insertionSort(int []arr){
        //1.找到无序的哪一组数组是从哪个索引开始的。
        int startIndex=1;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>arr[i+1]){
           break;
            }
            startIndex++;
        }

        for (int i = startIndex; i < arr.length; i++) {
            for (int j = i-1; j>=0; j--) {
                //如果startIndex大于有序的最后一个元素,不用对startIndex进行交换
                //思考一下这里为什么不用 arr[i]>arr[j]   ???             

                if(arr[j+1]>=arr[j]){
                    break;
                }
                //交换位置
                int t=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=t;
            }
        }

    }

3.插入排序还可以进行改善

public static void insertionSortImproved(int []arr){
        //1.找到无序的哪一组数组是从哪个索引开始的。
        int startIndex=1;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>arr[i+1]){
                break;
            }
            startIndex++;
        }

        for (int i = startIndex; i < arr.length; i++) {
            int j=i;
            int temp=arr[j];
            while(j>0&&temp<arr[j-1])
            {
                arr[j]=arr[j-1];
                j--;
            }
            arr[j]=temp;
            
            }

        }

4.每次写插入排序都有新感受

 //1.找到索引位置顺序
        int startIndex=1;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>arr[i+1]){
                break;
            }
            startIndex++;
        }
        //2.将索引处的元素插入到前面的有序数组中
        for (int i = startIndex; i <arr.length ; i++) {
           //3. 如果要插入的数比它前面的第一个数大,直接跳出循环。其实也可以不写下面这行,因为运行它下面的代码不会对结果造成影响
            if(arr[i]>arr[i-1]){break;}


          //4.去给要插入的数找寻插入位置
// 注意:while循环中进行比较时,应用 要插入的值 去跟后面比较,不能用arr[j]<arr[j-1],
            int j=i;int insertValue=arr[i];
            //j-1>=0 来保证索引不越界,也可以写j>0,但我推荐用j-1,因为在希尔排序中 需用j-gap来判断索引是否越界
            while(j-1>=0&&insertValue<arr[j-1]){
                arr[j]=arr[j-1];
                j--;
            }
            arr[j]=insertValue;

        }
    }

4.快速排序(不熟悉)

 1.先让end去移动,找到一个比基准数小的元素,然后才能去移动start来找到一个比基准数大的元素进行交换。

public static void quickSort(int []arr,int i,int j){
        //我的思路:把比基准数大的元素放在右边,反之放在左边。
         if(i>=j){return ;}
        //第一次先让 第一个元素作为基准数
        int start=i;
        int end=j;
        //pivotKey是基准数,注意pivotKey是数组中的元素,并不是索引
        int pivotKey=arr[i];
        //先向左移动 end 找比基准数小的数


        //因为一趟可能会 交换好几次start和end ,外循环要定义一个start!=end
        while(start!=end){
            //在这里arr[end]可以 大于或大于等于 pivotKey
            while (arr[end] > pivotKey&&end>start ) {
                end--;
            }
    //移动start 找比基准数大的数
    //在这里,arr[start]必须小于等于pivotKey
    //举个例子,当arr[]={6,6,6,2,3}
            while (arr[start] <= pivotKey && start < end) {
                start++;
            }

//当start和end到达一个位置后,令 基准数与arr[high]或arr[start]交换位置
        swap(arr,start,end);
}
        //最后还需要将基准数与start或end交换位置
       swap(arr,i,start);
       quickSort(arr,i,start-1);
       quickSort(arr,start+1,j);

    }
    public static void swap(int[]arr,int index,int index2){
        int t=arr[index];
        arr[index]=arr[index2];
        arr[index2]=t;
    }

5.希尔排序(直接插入排序的改进)

5.1算法介绍

5.2算法实现

每次进行分组,然后在各个组内进行直接插入排序 。

继续分组,直至组数为1后,进行最后一次排序。

public static void shellSort(int []arr){

        int length=arr.length;
        //外循环进行 进行分组

        for(int gap=length/2;gap>=1;gap/=2){

            //需要找到要插入组中的元素
            /*1.我们可以 以每一组的第一个元素,作为一个有序序列,然后将每组中后面的元素插入到前面的有序序列中
     *        2.但每组后面的元素应该怎么确定呢?就利用gap这个间隔,gap为第一组的第二个元素,gap+1是第二组的第二个
     *          例{1,2,3,4,5,6,7,8,9}
     *             gap=9/2=4
     *             {1,5,9}一组 {2,6}一组
     *             {3,7}一组,{4,8}一组
     *  */
            for(int i=gap;i<length;i++)
            {
                //记录被插入数
                int insertValue=arr[i];
                //由于在找寻插入位置时,i会发生改变,所以要用j去代替它的作用
                int j=i;
                while(j-gap>=0&&insertValue<arr[j-gap])
                {
                    //进行移位
                    arr[j]=arr[j-gap];
                    //j-=gap;意味着 插入点 变为 从当前位置变为 本组中前一个位置。
                    j-=gap;
                }
                //把插入值填进去
                arr[j]=insertValue;
            }

        }
    }

二十三. 算法API

 1.Arrays.toString()源码

参数不止是 short类型的数组,还有其他的重载函数

public static String toString(short[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    }

2.Arrays.binarySearch()

注意:使用这个方法的前提是  数组是有序的,不然返回的索引会有很大的问题。

我再解释一下为什么    -插入点  还要再减去1 ?不减1不行吗?

引入一个场景,

①要查询的值 恰好在 0索引,这时会返回0

②在数组中并未查到此值,于是去返回插入点,插入点恰好在0索引 ,此时 -0=0

无法区分返回的索引0 是数组中存在的值,还是此值的插入点。

综上,当数组中不存在 要查询的值时,返回 负的插入点的索引-1

 3.Arrays.copyOf(老数组,新数组的长度)     该方法返回新数组的地址,需用数组来接收。

 4.Arrays.copyOfRange(老数组,int from,int to);

注意点跟Arrays.copyOf()相同

5. Arrays.fill(数组,值)

6.Arrays.Sort()  !!!重点

6.1默认情况

 6.2按指定的规则排序  (只适用于引用类型)

 

 

 注意重写的方法中    不要直接返回0,如果直接返回0就缺乏了o1与o2之间的比较,需要

return o1-o2;  逆序

return o2-o1;  正序

二十四.lambda表达式

1.简单介绍(@FunctionInterface注解很重要)

@FunctionInterface注解可以检验错误,即检测是否是 接口并且接口中只有一个抽象方法

 

2.省略写法

3.小练习

3.1匿名内部类写法

 3.2lambda表达式写法

二十五.五道算法题

1.跟排序有关

 备用知识:String类的  compareTo()方法

步骤如下:

 2.剩余跟递归有关,就不写了

二十六. 集合进阶

1.集合体系结构

下图中 红色框的是接口  ,蓝色框的是实现类

2.单列集合Collection

 2.1   boolean add()方法

2.2 void clear()方法     boolean remove(E e)方法

2.4  boolean contains()       很重要,对于自定义对象,要重写equals()方法

 2.5  判断集合长度是否为零

3.遍历方式

背景:由于 set接口下的实现类  无索引,不可重复,无序

因此 ,一般的for循环只能给  list接口下的实现类使用。

为解决此问题,我们要引入其他的遍历方式。

3.1迭代器

遍历时不能用 集合的移除方法去移除元素,只能用 迭代器的移除方法移除元素。

 细节注意点:迭代器遍历时不能用集合的方法进行删除或增加

代码演示:

 

 

 

3.2增强for遍历

3.3 lambda

 

 4.List集合常用的方法

注意 Collection的方法也被List继承了,想remove(Object o) 移除对象,remove( int index)移除索引。

但当用Integer包装类后,事情就变得有趣起来,你怎样区分 remove(1)  这个1是int类型还是Integer引用类型。

4.2 List系列集合的两个删除的方法

Integer i=1;  或用 Integer i=Integer.valueOf(1)

直接用第一种写法就行,第二种没什么用

 

 4.3list的遍历方式

 4.4列表迭代器(特有)注意写法

previous()有局限性,因为索引一般从 0 开始 ,这时无法指针无法前移来获得元素。

自己实践:值得注意迭代器的删除方式 和 ArrayList的删除方式不同。

 ArrayList<Integer> arrList = new ArrayList<Integer>();
        for (int i = 0; i < 5; i++) {
            arrList.add(i);
        }
        Integer i = 1;
        //ArrayList的移除方式,并且它有两个移除方式,里面要有参数,而且两个方式不一样
        //移除 Integer类型的元素,注意不是索引
        arrList.remove(i);

        Integer integer;
        //记得写<Integer>
        ListIterator<Integer> lit = arrList.listIterator();
        while (lit.hasNext()) {
            integer = lit.next();
            if (integer == 2) {
                //ListIterator<E>的移除方式 remove() ,里面不需要参数
                lit.remove();
            }
        }
        System.out.println(arrList);
    }

4.5总结

5.ArrayList源码(抽空实现)

6.LinkList源码 和 迭代器源码

二十七.泛型

1. 回首过去,没有泛型。

 

 2.泛型的好处

但它是假泛型,在编译期间就 把 异常避免,但在字节码文件中 AraryList会发生泛型擦除。

 3.泛型类

 3.泛型方法

4.泛型接口

4.1实现类给出具体类型。

 

 4.2实现类继续延续泛型。

在创建实现的对象时,再规定类型。像ArrayList一样,记得在括号内写上 引用类型

 

 5.泛型不具备继承性

这个数据可以理解为 放在列表中 的 引用数据类型,众所周知,类具有继承性。

 5.1何为数据,何为泛型

下面我给出一个method方法,形参为 泛型

//当形参类型为是一个泛型,泛型里面写的是Grandpa,那么传进去的实参括号中的类型也必须是Grandpa,即使Son和Father是子类也不行。

//但当形参不是泛型,而是像Grandpa g 这样的形参,那么就可以将 Son 类和Father类的对象传进去。对这就是多态。

 完整代码如下:

  ArrayList<Grandpa> list = new ArrayList<>();
        ArrayList<Dad> list2 = new ArrayList<>();
        ArrayList<Son> list3 = new ArrayList<>();
        //当形参类型为是一个泛型,泛型里面写的是Grandpa,那么传进去的实参括号中的类型也必须是Grandpa,即使Son和Father是子类也不行。
        method(list);
        method(list2);
        method(list3);

        //但当形参不是泛型,而是像Grandpa g 这样的形参,那么就可以将 Son 类和Father类的对象传进去。对这就是多态。
        method2(new Grandpa());
        method2(new Son());
        method2(new Dad());
    }

    public static void method(ArrayList<Grandpa> list) {

    }

    public static void method2(Grandpa g) {

    }
}

class Grandpa {
}

class Dad extends Grandpa {
}

class Son extends Dad {
}

6.泛型方法的弊端和使用 通配符 解决问题

 注意写法

7.总结

 

8.平衡二叉树(很不熟悉)

8.1左旋与右旋

左旋后

右旋

 

9.平衡二叉树需要旋转的四种情况

 9.1左左 (一次左旋)

 9.2左右(先左旋再右旋)

 

 9.3右右(一次左旋)

9.4右左(先局部右旋,再左旋)

 

 先局部右旋

 再整体左旋

 

末尾:发现自己对protected 权限修饰符不太了解

①创建一个包 protect ,里面一个 Animal类,一个Dog类,一个Test测试类,Dog类继承于Animal类

二十八.Set集合系列

1.简单介绍

 

 2.添加元素的返回值可能为false

 3.小结

4.HashSet

 

 

 4.2

 

 因为默认是根据 地址 计算 哈希值,因此要重写hashCode方法,去比较根据对象的属性生成hash值。而且重写equals方法很有意义,能够比较属性值是否相等。

 4.3三个为什么

问题1:

当存元素时,会根据hash值来存,下面是要存的索引位置

 所以这个索引位置 是 无规律性的,当进行遍历时,从0索引开始遍历,看0索引元素下有没有悬挂元素,若悬挂则进行遍历,然后遍历1索引,依次遍历

 问题2:

不是有个数组吗?怎么不能用索引?

因为一个数组的一个索引 下挂有 多个元素 ,你没法通过一个 索引 保证获得 特定的元素

 

 问题3:

数据去重是个很有趣的东西,对于自定义的对象,我们通过重写hashCode方法来 根据属性值计算hash值,然后进行 下面的计算 来存在此索引

①若该索引中的元素值为null,则将 要存的元素存进去

②若非空,则将要存的 元素 依次与该索引下的所有元素去 用equals()方法比较属性值是否相等,IF相等,则不用存; IF不相等,则将 要存的元素 挂在最下面。

5.LinkedHashSet

 

6.TreeSet

6.1简单介绍

6.2第一种排序方式,类实现Comparable接口,并重写里面的方法

 ①在这里可以回顾一下,当接口是泛型接口时,可以怎样 创造实现这个接口的类。

一个方法是 不延续泛型,在implements interface<类型>,指定类型是什么

一个是像下图一样,使用泛型类,延续泛型

 注意this.o是要添加的元素

 

 6.3第二种排序方式(通过构造方法指定排序)

 

 

 

6.4它跟Arrays,sort(数组,指定排序方式) 很像

但不用实现接口

 

6.5细品lambda表达式

 所有符合条件的匿名内部类都可以  写成 (参数)-> { } 的形式,不需要关心它在哪个方法的形参里,我原来理解错了,以为forEach和lambda表达式是相互关联的所以 可以写出  (参数)-> { } 的形式,但其实是因为 实参为 匿名内部类的原因 ,只要是符合条件的匿名内部类,都可以这样写。

6.6两种排序方式 何时用

对于我们自定义的对象,我们好像用这两个指定排序方式都可以,因为我们可以重写compareTo()方法 或去 在创建集合时自定义Comparator比较器对象。

但对于String类,Integer类等类 ,java已经事先将这些类的compareTo()方法重写了,因此会按照java的规则去比较。

当我们想打破枷锁去让String类换种方式去比较,就可以利用Comparator比较器。

下面去看一段代码:但自己写的Comparator 运行的结果有些不尽人意

 TreeSet<String> ts = new TreeSet<>();
        ts.add("aa");
        ts.add("ba");
        ts.add("c");
        ts.add("ab");

        for (String t : ts) {
            System.out.print(t + " ");
        }
        //当我们想比较字符串的长度
        System.out.println();
        TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
        ts2.add("aa");
        ts2.add("ba");
        ts2.add("c");
        ts2.add("ab");
        for (String s : ts2) {
            System.out.print(s + " ");
        }

 运行结果如下:怎么回事,我自己写的Comparator 最后  运行的 时候,我向TreeSet ts2 添加了4个元素,结果却只出现两个。

这是因为 TreeSet去重的结果,当返回0,代表比较的结果相等,就不会把元素添加进去。

AMAZING!!!!

 

 

6.7小总结

 

②创建一个包 protect2,里面一个Cat类,一个Test测试类,Cat继承于Animal类

③在 包protect 里面调用 Dog类创建的对象的  eat()方法。

父类的eat()方法 被 protected修饰,子类继承后该方法后,可以在本包中任何类中 或其他包的子类 ,使用该方法。 但如果在 另一个类里创建对象,则会报错

如下图,Dog类与Animal类位于一个包中, 所以在protect包中 Test类中创建Dog的对象并调用protected修饰的方法 不会报错

 

但在 protect2包中,由于Cat是Animal的子类,因此在Cat类中创建Cat类的对象并 使用父类的protected修饰的方法,不会报错。

但对于protect2包中的Test类,它并不是Animal的子类,因此在创建Cat类的对象后,并不能调用eat()方法。会报错!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值