JAVA基础学习笔记

尚硅谷课程,一月份断断续续看,仅供自己查看,没有他用
格式的话我后续查阅的时候再逐项改

day 1 继承 封装 多态
1.static
静态方法:不依赖于任何对象即可进行访问
非静态方法:必须依赖具体的对象才能够被调用
依赖具体对象的概念:
java中类和类之间有五种关系:依赖、关联、组合、聚合、继承(实现)
(1)依赖:一个类A中的方法使用到了另一个类B
比如:在类A中用到了类B的实例
代码:老王开车去东北,A类:老王;A类方法:开车; B类:车 ;B类方法:车跑路run;
那么在A类方法中就要调用B类实例,并且会用到B类方法。
B类的改变可能会影响到A类的结果。(所以关系较弱)
(2)关联:被关联类B以类属性的形式出现在关联类A中,或关联类A引用了一个关联类B的全局变量
代码:关联关系一般使用成员变量来实现
(3)聚合(has-a):关联关系的特例,与一般关联关系具有语义级别的区别。一般指代二类之间有”拥有“的语义。两个类之间的语义关系不是对等的。
(4)组合(contains-a):关联关系的特例,比聚合更强,强语义关系,关联不可分割。
(5)继承:对于两个类A和B,只有当两者之间确实存在 is-a 关系的时候,类B才应该继承类A。
”少用继承,多用组合“:对于不需要将父类所有方法都实现/重新实现的子类来说,这个准则是有道理的。一般抽象类会多用继承。
继承是白盒式代码复用、组合是黑盒式代码复用。

在非静态方法中可以访问静态/非静态;
在静态方法中不能访问非静态,只能访问静态。
2.静态变量
静态变量被所有的对象共享,内存中只有一个副本。
非静态变量是对象所拥有的,创建对象时即被初始化,存在多个副本,各个对象拥有的副本相互不影响。
3.static 代码块
形成静态代码块以优化程序性能。类中的static代码块在类初次被加载时,会按照顺序被执行且仅执行一次。
初始化的顺序 静态代码块 > 构造代码块 > 构造函数
举例:

class Person{
    private Date birthDate;
     
    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        Date startDate = Date.valueOf("1946");
        Date endDate = Date.valueOf("1964");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

👇

class Person{
    private Date birthDate;
    private static Date startDate,endDate;
    static{
        startDate = Date.valueOf("1946");
        endDate = Date.valueOf("1964");
    }//因为如果如上面那样,会每次在Person类加载的时候就会多次生成startDate与endDate,造成了内存空间浪费。
 public Person(Date birthDate) {
        this.birthDate = birthDate;
    }
     
    boolean isBornBoomer() {
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
}

因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

引用:https://www.cnblogs.com/gxyandwmm/p/9478569.html

3.Set引发的回顾
在考虑并发的情况下使用Vector(开销大,线程安全)
不考虑并发的情况下使用ArrayList(开销小,不能保证线程的安全)

4.异或运算(不进位)
异或运算就是:
任何数 异或 0 = 该数;
任何数 异或 该数自己 = 0;
所以想要检查一个数组中哪个数没有重复两次,用异或就可以得出最后答案,都重复偶数次后就结果为0;

5.HashSet
基于HashMap实现的,HashSet的底层是采用HashMap来保存所有的元素,就是将hashSet中的元素保存为其key,定义一个虚拟的Object对象:静态的Object 对象 作为HashMap的value。

在list、set与array(数组)转换的时候,传入的数组必须是一个引用类型(Integer[] args) 才能将其转为list、set,不然如果是基本类型数组时(int [] args)就会出现打印转换后的list与set时打印出其数组首地址,形似[[I@1540e19d])。
所以就是说当有一个基本类型数组的时候是没有办法将其转化为一个集合或者list的。
https://www.cnblogs.com/zengtao614/p/10727071.html

🔑:将基本类型数组转化为集合的方法:
①将基本类型数组->包装类类型数组:
org.apache.commons.lang3.ArrayUtils.toObject(int []):将int[]转换为Integer[]
eg:

Integer[] is = ArrayUtils.toObject(int[]);
List<Integer> asList = Arrays.asList(is);
System.out.println(asList);

②JDK8可以试试的方法:

List<Integer> list = Arrays.stream(src).boxed().collect(Collectors.toList());

🔑:将集合转为数组的方法:
①集合的toArray()方法:
先new一个长度与Array一样的空数组,然后:list.toArray(该数组)。如果toArray()无参数方法,那么就会返回Object类型的数组。

6.Collection的主要方法:
boolean add(Object o):添加对象到集合
boolean remove(Object o):删除指定的对象
int size():返回当前集合中元素的数量
boolean contains(Object o):查找集合中是否有指定的对象
boolean isEmpty()判断集合是否为空
Iterator iterator()返回一个迭代器
boolean containsAll(Conllection c)查找集合中是否有集合c中的元素
boolean addAll(Collection c) 将集合c中所有的元素添加给该集合
void clear()删除集合中所有的元素
void removeAll(Collection c)从集合中删除c集合中也有的元素
void retainAll(Collection c)从集合中删除集合c中不包含的元素

尽量返回接口而非实际的类型:
eg:返回List而非ArrayList,因为以后返回LinkedList时,因为返回的是 List,那么客户端的代码就不用改变,这就是针对抽象编程。
https://www.cnblogs.com/taiwan/p/6954135.html
ArrayList、HashSet/LinkedHashSet、PriorityQueue、LinkedList是线程不安全的,可以使用synchronized关键字,或者:

List list = Collections.synchronizedList(new ArrayeList(.....));

7.基本类型
自动类型转换:表示范围小的类型可以向表示范围大的类型转换,如long转为float,因为float的表示范围比long大;
基本类型常见问题:
eg:long total = 34* 34556234555;这个数应该不在int的表示范围里,应该在long的范围里,但是数值自然为int型,所以会溢出,所以应该改为:
long total = 34L
34556*234555;(将其中的任意一个数标为L(long)类型,那么得出的结果就是Long类型)

8.容器中存储的实际是变量(常量)的内存地址。所以删除的时候不是删除该值,而是该值存进来的地址。

Day2

1.JVM(Java virtual machine):实现一次编译,到处运行
JAVA垃圾回收机制:内存泄漏和内存溢出还会出现,垃圾回收机制无法回收
JDK(Java development kit):包含JRE+Java 开发工具集(例如Javac编译工具等)
JRE(Java runtime Environment):JVM+Java SE标准类库

2.byte、char、short–>int–>long–>float–>double–>
int + String =>String
string不能强转为int,要用Integer.parserInt(string)

3.二进制:以0b或0B开头
八进制:以0开头
十六进制:0x或0X开头, eg:)x21AF

4.一维数组的声明和初始化:数组一旦初始化完成,其长度就确定了
静态初始化

 int[] ids;
ids = new int[]{1001,1002,1003,1004};

String[] names = new String[]{"阿旺“,”阿喵“}
动态初始化

String[] names = new String[length];

元素类型决定数组初始化值:int[] -> 0;short->0;float->0.0; double->0.0;char->0或‘\u000’,非’0’,打印出来是空格
数组的内存解析:
栈存放:局部变量
堆存放:new出来的结构,如对象、数组
方法域:包括常量池与静态域

5.多维数组的声明和初始化:
二维:
静态初始化:int[][] arr1 = new int [][]{{1,2,3},{4,5},{6,7,8}};
动态初始化:①String[][] arrr2 = new String[3][2];
String[][] arr3 = new String[3][];
int[] arr4 = {1,2,3,4};//OK
int[] arr5; arr5={1,2,3,4};//NO
调用指定位置元素:注意NullPointer的错误
长度:arr1.length=?//3 arr1[0].length == 3
注:int arr[][] = new int[4][3];
sysout(arr[0]);将得到其地址值[I@15db9742 #’ [ '一个表示一维,I表示int类型
未指定第二维数组的话,那么得到地址就是null

局部变量都放在栈里

int[][] arr = new int[2][3];
int[][] arr2 = new int[3][];
int[][] arr3 =  new int[][]{{1,2,3},{2,3},{1,2,2,6,3,34}};

🔑引用类型数据初始化都是null(new的都是引用型),引用类型变量中存得要么是null要么是地址值,也就是说,new出来的对象中存的要么是null,要么是地址值。
6.安装目录与workspace
workspace是存放代码的地方,自己设置
7.数组的复制

int[] array1,array2;
array1=.......
array2 = array1;//只能说是地址的赋值,而不是数组的复制

真正的数组的复制只能通过循环遍历复制:

int array2 = new int[array1.length];
for()....

8.二分法查找:前提必须有序
9.冒泡排序比较的是相邻两个元素

10.数组中的常见异常:①数组下标越界ArrayIndexOutOfBoundsException;
②NullPointerException空指针异常,一般指的是数组为赋值(初始化)

11.面向对象
1.Java类及类的成员:属性、方法、构造器、代码块、内部类
2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3.其他关键字:this\super\static\final\interface\abstract\import\package等

属性=成员变量=field=域、字段
方法 =成员方法=函数=method
如果创建了一个类的多个对象,每一个对象都独立地拥有一套类的属性(非static的)(初始化值)
同样,对象1 = 对象2;实则也是将对象的地址进行赋值,所以思考一哈。
对象的内存结构:
虚拟机栈(VM Stack):用于存储局部变量
堆:存放对象实例(New出来的都算)
方法区:存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据

成员变量(属性)与局部变量的区别:
①在类中声明的位置不同
属性:直接定义在类的一对{ }中;
局部变量:声明在方法内,方法形参,代码块内,构造器形参,构造器内部的变量;
②关于权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符;常用的权限修饰符:priavte、public、缺省、protected
局部变量:不可以使用权限修饰符的。
③默认初始化值得情况
属性:类的属性根据其类型都有初始化值
整型(byte、int、short、long):0
浮点型(float、double):0.0
字符型(char):0或’\u0000’)
布尔型(boolean):false
引用数据类型(类、数组、接口):null
局部变量:没有默认初始化值,意味着在调用其之前要显式地赋值,特别地,形参在调用时,我们赋值即可
④在内存中加载的位置不一样:
属性:加载heap空间中
局部变量:加载到stack空间中

day3

1.匿名对象的使用
创建的对象,没有显式地赋给一个变量名,即为匿名对象。

2.方法的重载
概念:在同一个类当中,允许存在一个以上的同名方法,但是方法内容以及参数个数不一样
总结:“两同一不同”,同一个类,同样的方法名,不同的方法内容与参数个数

3.可变个数的形参
数据类型… 变量名

String... strs == String[] strs;

但是使用时有差别:
对于String… strs的传参:“aa”,‘bb’,’‘cc’’
对于String[] strs 的传参:new String[]{“aa”,“bb”,“cc”};
对于String… strs的遍历:strs[i]
对于String… strs应该放在其他确定类型参数的后面:void mmm(int i, String… strs),可变个数的参数应该放到固定长度参数后面

4.方法参数的值传递机制
如果变量是基本数据类型,赋值时是将变量所保存的数据值进行传递;
如果变量是引用类型(new出来的),赋值时则是变量保存的数据的地址值。
当调用的方法中有形参,那么该形参也在stack中进行了创建,只不过是将实参的值传给了形参罢了:
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
5.封装与隐藏(面向对象特征之一)
需要避免用户再使用“对象.属性”的方式对属性进行赋值,并且需要设置setValue的方法对属性赋值进行一定的约束(作为外部数据流交互的接口),用getValue的方法来得到相应的值。

四种权限修饰符(封装性的体现,需要权限修饰符来配合):
public:同一个工程
protected:不同包的子类
缺省(default):同一个包
private:类内部

6.构造器(任何一个类都有构造器)

作用:用以创建对象
默认的构造器:没有显式地定义类的构造器的话,则默认系统会构造一个空参数的构造器
构造器的结构:

权限修饰符 类名(形参列表){

}

一个类中定义的多个构造器,彼此构成重载
一旦显式地定义了类的构造器后,系统就不再提供默认的空参构造器
一个类中至少要有一个构造器
默认构造器的权限与类的权限一致
7.JavaBean
如果有一个java类:类是公共的;有一个无参的公共的构造器;有属性,且有对应的get、set方法,那么他就是javaBean

8.this 关键字
this理解为当前对象
在类的方法中,我们可以使用“this.属性”或“this.方法”的方式 ,调用当前对象属性或方法,但是,通常情况下, 我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式地使用"this.变量"的方法,表明此变量是属性,而非形参。

this还能用来修饰构造器(防止重复写相同的构造器内容):
构造器中调用构造器:构造器中:this(有无参数依据构造器);
构造器中不能通过“this(形参列表)”调用自己
如果一个类中有n个构造器,则最多有n-1个构造器中使用了“this(形参列表)”,因为调用不能成环;
“this(形参列表)”必须声明在当前构造器的首行
构造器内部,最多只能声明一个”this(形参列表)",用来调用其他的构造器
9.MVC设计模式
视图模型层、控制器层、数据模型层

数据层:主要处理数据(model.bean/domain、model.dao、model.db)
视图层:view.utils
控制层:controller.activity
10.import
Java 核心包在java.lang包中,不需要导入包了,直接省略import即可。
如果使用的是本包内的类或接口,则可以省略import
XXX.不包括XXX包的子包。
import static:导入指定类或接口中的静态结构。import static的落脚点必须是类中或者接口中,而不是一个类或者一个接口。所以import static java.lang.
;

10.继承————面向对象的特征之二

一、继承性的好处:
①减少代码冗余、提高了代码的复用性
②便于功能的扩展
③为之后的多态的使用,提供了前提

二、继承性的格式:class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有结构:属性、方法
特别的,父类中声明为private 的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为有封装性的影响,使得子类不能直接调用父类的结构而已。
2.2子类继承父类之后,还可以定义声明自己特有的属性和方法。子类与父类的关系不同于子集和集合的关系。子类往往更为丰富一点。
2.3Java中关于继承的规定:
①一个类可以被多个子类继承;
②一个类只能有一个父类:单继承;
③ 子、父类是相对的概念
④子类直接继承的父类,称为:直接父类。间接继承的父类成为:间接父类
⑤子类继承父类以后,就继承了所有父类包括间接父类中的所有结构。
三、1.如果没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类
2.所有的java类都直接或见解地继承java.lang.Object类。

day4

1.方法的重写(overwrite/override)
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。程序执行时,子类的方法将覆盖父类的方法。
区分方法的重载与重写:

重写的注意事项:
①子类重写的方法的方法名与形参列表与父类被重写的方法的方法名和形参列表相同;
②子类重写的方法的权限修饰符不小于父类的被重写的方法的权限修饰符
特殊情况:子类中不能重写父类中权限为private的方法,但是可以写,就不算是重写罢了

③返回值类型:
如果父类中的方法返回void,那么子类重写其的类型也只能是void;
如果父类中的方法返回是A类,那么子类重写的方法返回类型为A类或A类的子类
如果父类中的方法返回是基本数据类型,那么子类重写的方法返回类型必须为相同的类型
④子类重写的方法抛出的异常不大于父类被重写的方法抛出的异常类型
⑤子类和父类中的同名同参数的方法要么都声明为非static的(才能考虑重写),要么都声明为static的(但 这就不是重写了),静态方法不能被重写的意思。

2.super关键字:父类的
用父类中被重写的方法,不会和子类中已经重写的相同名字的方法搞混。
用途:属性、方法、构造器
方法:super.属性的方式调用父类中定义的属性,习惯性省略super.
当在子类中定义了与父类中同名的属性时,要在子类中调用父类中该属性时,则必须加上super.
方法与构造器同上。super(形参列表)来调用父类中相同形参列表的构造器。必须声明在子类构造器的首行。并且在类的构造器中,this(形参列表)与super(形参列表)只能二选一,不能同时出现。
当父类中某个属性为private的时候,除了getXXXX方法可以得到,也可以用super()来对该private属性进行赋值。
在构造器的首行,既没有显式地调用this(形参列表),又没有调用super(形参列表),默认调用了super(形参列表)。
当一个类被继承时(即其成为子类),那么它将必须要显式地写出空参的构造器。(没有人继承的时候,它空参的构造器可以不写,但是默认是存在的)
一个类的构造器中不是super()就是this(),调用父类或者自己的构造器。

3.子类对象实例化过程
①子类继承父类后,就获取了父类中声明的属性和方法;创建子类的对象,在堆空间中,就会加载所有父类中声明的属性;
②当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器;直到调用了Java.lang.Object类中空参的构造器位置。
注:虽然创建子类对象时调用了父类的构造器,但自始至终只创建过一个对象,即为new的子类对象;

4.多态——面向对象的三大特性
①一个事物的多种形态:父类的引用指向子类的对象!(子类的对象赋给父类引用)
举例:Person p2 = new Man();其中Man是Person的子类;
②当调用字符类同名同参数的方法时,实际执行的时子类重写父类的方法——虚拟方法使用;
③多态的使用:虚拟方法调用,有了对象的多态性之后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译时(能否调用)看左边的类,运行(最后的结果)看右边的类。
④多态性的使用前提:a.有类的继承关系;b.方法的重写;
why:个人理解,将一个方法可以输入多种关联类参数的用途,类似于形参的多态
⑤对象的多态性只适用于方法,不适用于属性;(编译和运行都看左边)

5.提前进入集合
①集合、数组都是对多个数据进行存储操作的结构,简称java容器;(此时的存储主要指的时内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi))
a.数组在存储多个数据方面的特点:
一旦初始化之后,其长度就确定了;
数组一旦定义好,其元素类型也确定了;
b.数组在存储多个数据方面的缺点:
一旦初始化之后,长度不可更改;
数组中提供的方法非常有限,对于添加、删除、插入数据等操作非常不便,同时效率不高;
获取数组中实际元素的个数,数组没有现成的属性或方法可用。
数组存储数据的特点:有序、可重复;对于无序、不可重复的需求不能满足
二、集合框架

|-----Collection接口:单列集合,用来存储一个一个的对象
|---------List 接口:存储有序的、可重复的数据  -->“动态”数组
|------------ArrayList、LinkedList、Vector
|---------Set接口:存储无序的,不可重复的数据   --->高中数学中的集合
|------------HashSet、LinkedHashSet、TreeSet
|-----Map接口:双列集合,用来存储一对(key,value)一对的数据
|-------------HashMap、LinkedHashMap、TreeMap、HashTable、Properties

三、Collection接口中的方法的使用
向Collection接口的实现类的对象中添加obj时,要求obj所在类要重写equals()。
注:当重写了特定类中的equals方法就会发现,在删除Collections中的元素时可以通过new 对象的方法来进行删除。
1.方法
①removeAll:求差集
②retainAll(Collection coll2):交集,获取当前集合和coll2集合的交集,并返回给当前集合
③toArray(coll):集合->数组,得到的是Object[]
④Arrays.asList(array):返回的是ArrayList
注:List arr = Array.asList(new int[]{123,456})这样会认为arr是包含一个数组元素的list,就只有{123,456}这样一个数组列表;
List arr = Arrays.asList(new Integer[]{123, 456})这样可以,是包含123,和456两个元素的list;或者List arr = Arrays.asList(123,456);
⑤iterator():返回Iterator接口的实例,用于遍历集合元素(iterator不是容器噢,可以理解为一种指针)

       Iterator iterator = coll.iterator();
	   while(iterator.hasNext()) {
			System.out.println(iterator.next());
	   }

当用iterator.next到了list的最后位置,再利用hasNext就可能不行了,得重新得到coll的iterator。
iterator的remove方法不等于Collection的remove方法,iterator的remove方法相当于针对指针所指的位置进行remove,所以不能连续多次iterator.remove,也不能先remove再next。
⑥foreach增强for循环用于遍历集合、数组

for(object obj :coll){
	内部原理还是iterator 
}

2.List接口:存储有序、可重复的数据。替换原有的数组。

|-----Collection接口:单列集合,用来存储一个一个的对象
|---------List 接口:存储有序的、可重复的数据  -->“动态”数组
|------------ArrayList:作为List接口的主要实现类,执行效率高,线程不安全的;底层使用Object[]存储;
|------------LinkedList:对于频繁地插入、删除操作,使用此类效率比ArrayList高;底层使用的是双向链表存储;
|------------Vector:作为List接口的古老实现类,执行效率低,安全的;底层使用Object[]存储。

面试题:ArrayList、LinkedList、Vector三个之间的异同:
同:三个类都实现了List接口,存储数据的特点相同:有序、可重复的数据
不同:见上图

②ArrayList源码 :jdk7情况下

ArrayList list = new ArrayList();//底层创建了长度为10的object[]数组 elementData
list.add(123);
....
list.add(11)//如果此次添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的容量为1.5倍,同时需要将原有数组中的数据复制到新的数组中。

结论:建议开发中使用带参的构造器:

ArrayList list = new ArrayList(initialCapacity);

ArrayList源码 :jdk8情况下

ArrayList list = new ArrayList();//底层创建object[] elementData {},并不是初始化长度为10
list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData中,后续的添加和扩容操作与jdk 7无异;

LinkedList的源码分析:

LinkedList list = new LinkedList();//内部声明了一个Node类型的first与last属性,默认为null
list.add(123);//将123封装到Node中,创建了Node对象;
Vector的源码:扩容为两倍

3.List接口的常用方法

4.Set接口:无序的,不可重复的(没有额外定义新的方法,都是Collection里定义过的)

|-----Collection接口:单列集合,用来存储一个一个的对象
|---------Set接口:存储无序的,不可重复的数据   --->高中数学中的集合
|------------HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
|-------------LinkedHashSet:作为HashSet的子类,遍历其内部数据时可以按照
添加的顺序遍历;
|-------------TreeSet:以红黑树的结构存储,使得存储的对象要求是同一个类,按照某些属性进行排序的。

①Set的无序性:无序性≠随机性,所以每一次遍历的顺序是一样的,只是在存入的时候按照计算出的hashcode来排,所以与我们添加时的顺序不一样。
以HashSet为例说明,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的hash值决定。
②Set的不可重复性:保证添加的元素按照equals()判断时,不能返回true。即相同的元素不能同时存在。
③HashSet添加元素的过程:
④Linked HashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用来记录此数据的前一个和后一个数据
Set set = new LinkedHashSet();
优点:对于频繁的遍历操作,LinkedHashSet效率高一些
⑤TreeSet
向TreeSet添加元素必须是相同类的元素
两种排序方式:自然排序(实现CompareTo()接口)和定制排序(Comparator)
自然排序中,比较两个对象是否相同的标准为CompareTo()方法的返回值是否为0,而不再是equals方法。定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals.
四、Map
1.

|-----Map接口:双列集合,用来存储一对(key,value)一对的数据
|-------------HashMap:作为map的主要实现类;线程不安全的,效率高;存储null的key和value
|------------LinkedHashMap:保证遍历map元素时,可以按照添加的顺序实现遍历;
原因:在原有的hashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap;
|-------------TreeMap:保证按照添加的key-value对进行排序,实现排序遍历;此时考虑key的自然排序,底层是红黑树实现的。
·两种排序方式:自然排序与定制排序

|-------------HashTable:古老的实现类;线程安全的,效率低的;不能存储null的key和value
|-----------Properties:常用来处理配置文件,key与value都是String类型。

HashMap的底层:数组+链表(jdk7之前)
数组+链表+红黑树(jdk8)

2.Map结构的理解:

Map中的Key:无序的、不可重复的、使用Set存储所有的key;(是什么map就用什么set去存储key)----->key所在的类要重写equals方法和hashcode方法。
Map中的Value:无序的,可重复的,使用Collection存储所有的value;---------->values所在的类可能要重写equals;
一个键值对:key-value构成了一个Entry对象;
Map中的entry:无序的、不可重复的,使用Set存储所有的entry;

3.HashMap的底层实现原理(jdk7)

Hash Map map = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[] table;
....map.put(key1,value1)....
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法之后得到在entry数组中的位置:
①如果此位置上的数据为空,此时的key-value1添加成功-----情况1
②如果此位置上的数据不为空,意味着此位置上存在一个或多个数据,则要比较当前key1
与已经存在的数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功-------情况2
如果key1的哈希值与已经存在的某一个数据的哈希值相同,继续比较:调用key1所在类的equals方法,比较:
如果equals()返回false:key1-value1添加成功------------情况3
如果equals()返回true:使用value1替换相同key的value值(修改功能)
在不断添加的过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2

jdk8的情况:

1.new HashMap():底层没有创建一个长度为16的数组
2.jdk 8 底层的数组是:Node[], 而非Entry[]
3.首次调用put()方法时,底层创建长度为16的数组
4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
 			当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储。
4.LinkedHashMap的底层实现原理

注:HashSet的底层就是HashMap,将set中的值放到HashMap中的key中。
5.Map中的常见方法

*添加删除修改
6.Map的key与value打印(iterator是针对collection讲的)
7.向TreeMap中添加key-value,要求key必须是由同一个类创建(因为要按照key进行排序:自然排序、定制排序)
9.Hashtable中的Properties:常常用来处理配置文件。key和value都是String类型。
10.Collections工具类的使用
Collections是一个操作Set、List、Map等集合的工具类(加重是因为Map是和Collection并列的一个概念)
面试题:Collection与Collections区别:Collection是一个接口,Collections是操作Collection、Map的工具类。
①reverse(list):反转List中的元素
②shuffle(list):对list元素进行随机排序
③sort(list):根据元素的自然顺序对指定list集合元素按升序排序
④sort(list, Comparator):根据指定Comparator产生的顺序对list集合元素进行排序
⑤swap(list,int, int):将指定list集合中的i处元素和j处元素进行交换
⑥copy():
List dest = new ArrayList();
Collections.copy(dest,list);
//这样会报异常
五.static
①静态变量随着类的加载而加载,因此可通过"类.静态变量"的方式进行调用;这也是与非静态的区别。
②静态变量的加载要早于对象的创建(生命周期的区别);
③由于类只会加载一次,则静态变量在内存中也只会存在一份;存在方法区的静态域中。
④调用

			类(静态)变量         实例(非静态)变量
类			  Yes			 			 No
对象			  Yes						Yes 

			静态方法        非静态方法
类			Yes			No
对象			Yes			Yes 

非静态属性通过对象调用;静态属性通过类就可调用;
⑤静态方法中,只能调用静态属性或静态方法;
非静态方法中,可以调用静态也可以调用非静态
⑥在静态方法中不能使用this关键字和super关键字;
⑦开发中,如何确定一个属性是否要声明为static:
属性不会随着对象的不同而不同,即所有对象公享
开发中,如何确定一个方法是否要声明为static:
操作静态属性的方法,通常声明为static的
工具类中的方法,习惯上声明为static的

day5 设计模式
一.单例模式
1.单例设计模式:即采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
2.如何实现
答:饿汉式:1.私有化类的构造器;2.内部创建类的对象;3.提供公共的方法,返回类的对象;4.要求此对象也必须声明为静态的。
懒汉式:1.私有化类的构造器;2.声明当前类的对象,未初始化;3.声明public、静态的返回当前类对象的方法;
3.区分
饿汉式:坏处 对象加载时间过长 ; 好处 饿汉式式线程安全的;
懒汉式:好处 延迟对象的创建 ; 坏处 懒汉式 线程不安全;----->多线程时可以更改为线程安全的。

二、代码块(类的成员,或称初始化块)
1.作用:通常用来初始化类、对象
2.要么不修饰,要么就只能用static修饰:静态代码块与非静态代码块
3.静态代码块:内部可以有输出语句,随着类的加载而执行
4.非静态代码块:随着对象的创建而执行;每创建一个对象就执行一次非静态代码块;可以在创建对象时,对对象的属性等进行初始化;如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行;
5.静态代码块的执行早于非静态代码块的执行(因为非静态代码块的执行需要实例化对象)

6.赋值的优先顺序
默认初始化>显示初始化/代码块中赋值>构造器中初始化>对象后通过”对象.属性“或者”对象.方法“进行赋值;
三.final关键字
可修饰类、方法、变量
1.final 类不可被继承:final class A{ }
2.final 方法不可被重写(override)
3.final修饰变量:此时的变量就称为一个常量了
修饰属性时,若要对final该属性进行赋值:①显式赋值;②代码块中赋值;③构造器中初始化;(其他的都不行)
修饰局部变量
4.static final用以修饰属性:全局常量

static:修饰属性、方法、代码块、内部类
final:修饰属性、方法

四、抽象类与抽象方法
1.abstract修饰类:抽象类
①一旦抽象类,则不可实例化
②抽象类中一定有构造器,便于子类实例化时调用
③开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作;
2.abstract修饰方法
①抽象方法只有方法的声明,没有方法体(为了让子类来进行多样化完善)
public abstract void eat();
②子类必须要实现父类(间接父类也算)中定义的所有抽象方法,才可以去实例化
子类没有重写父类中定义的所有的抽象方法,就必须是一个抽象类。
3.abstract使用注意方法:
不能修饰属性、构造器等结构
不能修饰私有方法、静态方法、final的方法(不能被重写),final的类(不能有子类)
4.创建抽象类的匿名子类对象

五、接口(可以达成一种多继承的效果,java不支持多继承)
1.Java中,接口和类是并列的两个结构;
①JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的,但是书写时可以省略public static final 不写
抽象方法:public abstract的
②JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
接口定义的静态方法只能通过接口来调用,不能实现类调用
interface.静态方法();
而不能 implementClass.静态方法();
接口定义的默认的方法可以通过实现类的实例对象去调用。
如果实现类重写了接口中的默认方法, 调用时,仍然调用的是重写以后的方法。
子类(实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法(类优先原则)。
但如果是属性的话就都调用。
如果实现类实现了多个接口,而这多个接口定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。–>接口冲突
这就要求我们必须在实现类中重写此方法。

2.接口中不能定义构造器,意味着接口不可以实例化
3.Java开发中,接口都通过类去实现的方式来使用(implements)
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化;
否则,则此实现类仍为一个抽象类;
4.Java类可以实现多个接口–>弥补了Java单继承性的局限性
如果有继承和实现,那么先继承再覆盖:
class AA extends BB implements CC{ }
5.接口与接口之间是继承关系,而且可以多继承
6.接口具体的使用体现多态性
7.实际上接口可以看作一种规范
面试题:抽象类与接口有哪些异同
8.继承与实现都能够得到父类或者接口中定义的属性和变量?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值