java基础篇
1、引用数据类型–String
string,即字符串,string类型是个不可变对象。可以理解为一个指针,指向对应的字符串内容,对string类型进行修改等同于将指针指向一个新的string对象,原先的字符串内容依然存在。当程序需要经常改变内容时,最好不要使用string类型,会浪费大量的内存空间。
以“ ”方式给出的字符串对象,在字符串常量池中存储,而且相同内容只会在其中存储一份。
若是通过new构造器得到字符串对象
与string相似功能的有stringbuffer、stringbuilder,与string不同的是,stringbuffer、stringbuilder修改是在自身上修改。
stringbuilder是一个可变的字符串类,可以把它看成一个对象容器。
stringbuffer:线程安全,速度相对stringbuilder较慢,更适用于多线程情况;
stringbuilder:线程不安全,速度更有优势,更适用于单线程;
多数情况下采用stringbuilder。
2、集合容器
集合是与数组类似,也是一种容器,用于装数据。
数组的特点是类型固定、长度固定;集合大小不固定,类型也可以选择不固定,适合于元素个数不确定且要进行增删的业务场景。
集合支持泛型,可以在编译阶段约束集合对象只能操作某种数据类型。集合中只能存储引用类型,不支持基本数据类型,即集合中存储的元素不是对象本身而是对象的地址,因而不支持ArrayList,支持ArrayList,Integer是int的包装类。
集合分为:
- collection单列集合,每个元素只包含一个值
- Map双列集合,每个元素为键值对
collection集合特点:
-
List系列集合:添加元素是有序、可重复、有索引;
ArrayList、LinkedList:有序、可重复、有索引
-
Set系列集合:添加的元素是无序、不重复、无索引
HashSet:无序、不重复、无索引;
LinkedHashSet:有序、不重复、无索引
TreeSet:按照大小默认升序排序、不重复、无索引
Collections并不属于集合,是用来操作集合的工具类。
迭代器:
在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
Iterator<String> it = 集合.iterator();
while(it.hasnext()){//还有下一个
it.next();//取元素
}
也可以使用增强for循环:
for( E e : 集合变量名){};//遍历
2.1 List
ArrayList是集合中的一种,支持索引,带有增删API。
List的实现类的底层原理:
- ArrayList底层是基于数组实现,根据查询元素快,增删相对慢;第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
- LinkedList底层基于双链表实现的,查询元素慢,增删首位元素快。
2.2 Set
set:无序(存取顺序不一致),不能含有重复值,无索引(没有带索引的方法),类似于数学中的集合。
Set集合底层原理:JDK8之前,哈希表:采用数组+链表;JDK8开始后,哈希表:采用数组+链表+红黑树。
HashSet底层原理:采取哈希表存储的数据
Set集合实现类特点:
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:排序、不重复、无索引
2.3 Map
key-value形式 => map[key]=value;
Map集合Lambda形式遍历:
maps.forEach((k,v) ->{
System.out.println(k+"----->"+v)''
});
HashMap的特点和底层原理:
- 底层是哈希表结构
- 依赖hashCode()方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode()方法和equals方法;
- 基于哈希表,增删查改的性能都较好。
LinkedHashMap特点和原理:
- 有序指的是保证存储和取出的元素顺序一致;
- 底层数据结构哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储的顺序。
TreeMap特点和原理:
- 可排序:按照键数据的大小默认升序排序,只能对键排序
- 底层原理与TreeSet一样。
3、OOP面向对象编程–三大特性
3.1封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
具体体现:权限修饰符private,修饰变量时需创建set、get方法。通俗而言,将控制权掌握在自己手中,我可以允许我的数据被读取和修改,但要限制我提供的权限中,你要修改我的数据就要用我提供给你的方式修改。
在代码上,无法通过 对象.变量= 来修改变量值。
**static关键字: **
不依赖于对象,跟随类存在。所有对象共用一个,即创建了多个实例化对象时,每个对象的该变量值相同,若过程中该值被修改,那么所有对象的该变量值也随之改变(方法同理)。
在JMM内存模型表现上,创建实例化对象时,所有对象都会指向同一个被static的变量/方法堆空间,即变量/方法在内存中只存储一份。
以成员变量为例
- 静态成员变量:有static修饰,属于类,内存中加载一次;推荐**类.变量 **进行修改或赋值。
- 实例成员变量:存在于每个对象中,通过对象.变量进行修改赋值。
内存图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPjHHDvz-1643512758550)(C:\Users\蔡晓娜\AppData\Roaming\Typora\typora-user-images\image-20220129105445679.png)]
static访问注意实现:
- 静态方法只能访问静态成员,不可以直接访问实例成员;
- 实例方法可以访问静态成员,也可以访问实例成员
- 静态方法中不可以出现this关键字(this指向当前对象,static依赖于类)
this关键字:
作用是指向当前对象的地址,可以通过this引用当前对象的成员。
**注意:**只能在构造方法中调用别的构造方法,并且调用只能在第一行。
3.2继承
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。已有的类称为父类、基类,派生的类称为子类。在java语法中只支持单继承。
子类访问采取就近原则:
子类局部成员范围 -> 子类成员范围 -> 父类成员范围;
即如果子类中出现重名的成员,会优先使用子类的,如果一定要在子类中使用父类的,可使用super关键字实现对父类成员的引用。
在代码上,子类构造器中super()这行代码写不写都会默认调用父类无参构造器。如果父类没有无参构造器,就需通过super调用父类有参构造器来初始化继承自父类的数据。其中super和this一样,都只能放在构造器的第一行,所以二者不能共同存在于一个构造器。
创建实例化对象时:先创建父类内容再创建自身内容 -> 父类静态方法 - 自身静态方法 - 父类构造方法 -自身构造方法。
子类可以在继承父类的基础上,重写父类方法,新增功能。
3.3多态
同类型的对象,执行同一个行为,表现出不同行为的行为特征。多态是同一个行为具有多个不同表现形式或形态的能力。
产生条件:继承、重写父类方法、创建一个指向子类的父类对象。
子类重写父类方法时,访问权限要大于等于父类(default < protected < public)。
多态常见形式:
- 父类类型 对象名称 = new 子类构造器
- 接口 对象名称 = new 实现类构造器
多态中成员访问特点:
- 方法调用:编译看左边,运行看右边;
- 变量调用:编译和运行都看左边。
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Aniaml a = new Dog();
a.run();//后续业务行为随对象而变,后续代码也无需修改
代码:public class cat extends animal{
//在继承的基础上可以新增方法,父类无法看到子类的新增功能
public void shout(){
System.out.println("喵喵");
}
}
多态允许将子类类型对象地址赋值给父类指针,表现出子类行为
1、animal a = new cat();
a.shout();
在内存表现上,一个父类指针指向了子类对象,也就能够调用子类方法。如下图3.3-1所示
2、cat b = new animal();
b.shout();//无法实现
在内存表现上,一个子类指针指向了父类对象,那么也就不能够调用子类独有的方法,父类是无法看到子类新增功能的,所能调用的方法仅限于父类含有。
3、假设改变b的指向:强制类型转换
b = (cat) a;
b.shout();//能够实现 转换成真正的子类类型
此时b指向子类对象,能够调用子类独有方法。如下图3.3-2所示
4、那么反过来呢,
a = b;
b.shout();//无法实现
a此时指向了父类对象,无法调用子类独有对象
图3.3-1
图3.3-2
instanceof 关键字:
判断是否是类的对象或者其子类对象,在同一继承链使用;返回类型为boolean类型。
可应用于判断是否出现异常而作出相应处理的场景:开发过程会出现各种各样的异常,利用 instanceof 关键字在出现某一类异常时作出处理。