# MyJavaSE **Repository Path**: xk857/MyJavaSE ## Basic Information - **Project Name**: MyJavaSE - **Description**: 软件集训队,javase授课笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-14 - **Last Updated**: 2021-03-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Day01 学习目标: - 基础不牢固,进度相对较慢。 - 包装类和基本数据类型间的区别 - equals和其他常用方法的使用 - ArrayList 集合的定义及使用(增删改查,查询集合大小等) - ArrayList 集合的两种遍历方式 (for循环和增强for) - 进度快,但底子不扎实,或者是代码完全按照"野路子"写,不考虑维护 - String和Integer的底层是如何实现的 - java内存空间堆栈的概念 - 包装类和基本数据类型在内存堆栈中是如何分配的 - **使用 ArrayList 模拟数据库存储数据** - 基础扎实,进度较快,对源码及新技术有追求 - ArrayList 底层是如何实现的?动态扩容机制是怎样的 - 手写简化版 ArrayList (包含构造函数(有参无惨)、add、==扩容机制== ) ## 包装类和基本数据类型 ![image-20210311115308995](http://xk857.com/blog/2021/02/image-20210311115308995.png) ### Integer和int 运行下面的代码: ```java public static void main(String[] args) { int a1 = 100888; int a2 = 100888; System.out.println(a1==a2); Integer b1 = 100888; Integer b2 = 100888; System.out.println(b1==b2); Integer c1 = 30; Integer c2 = 30; System.out.println(c1==c2); } ``` 思考为什么会出现这种情况? - a1和a1是基本数据类型,比对的是真实数据。 - b1和b2是引用数据类型,比对的是内存地址。 - c1和c2虽然是引用数据类型,但是因为Integer对象创建时,会复用已经存在的内存地址,所以 `-128 ~ 127` 可以正常进行比较。 ### String ```java private static void StringDemo01() { String a1 = "张三"; String a2 = "张三"; System.out.println(a1==a2); scString = new Scanner(System.in); String b1 = "张三"; System.out.println("请输入您的姓名"); String b2 = scString.next(); System.out.println(b1==b2); System.out.println(b1.equals(b2)); } ``` ### 常用方法 | 方法名 | 返回值 | 作用 | | -------------------------------------- | -------- | -------------------------------------------------- | | parseInt(String\|str) | int | 返回包含在由 str 指定的字符串中的数字 的等价整数值 | | toUpperCase(char\|ch) | char | 将字符参数转换为大写 | | toLowerCase(char\|ch) | char | 将字符参数转换为小写 | | isUpperCase(char\|ch) | boolean | 判断指定字符是否是大写字符 | | IsLowerCase(char\|ch) | boolean | 判断指定字符是否是小写字符 | | equals(Object\|obj) | Boolean | 将调用该方法的对象与指定 的对象相比较 | | 字符串名.toCharArray() | cahr[] | 字符串转char数组 | | String.split("字符") | String[] | String 根据分隔符转化成 String 数组 | | trim() | String | 去除左右空格 | | substring(int beginIndex,int endIndex) | String | 截取字符串 | | equalsIgnoreCase(String) | String | 比较,忽略大小写 | | contains(String) | boolean | 判断一个字符串里面是否包含指定的内容 | | startsWith(String) | boolean | 测试此字符串是否以指定的前缀开始 | ### 总结 - 包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址; - 包装类型是引用的传递;基本类型是值的传递 - 初始值不同。int的初始值为 0,boolean的初始值为false,包装类型的初始值为null - 存储位置不同:基本数据类型直接将值保存在值栈中;包装类型是把对象放在堆中,然后通过对象的引用来调用他们 ### 延伸:java内存空间堆栈详解 - stack栈: - Java中一个线程一个栈区,每一个栈中的元素都是私有的,不被其他栈所访问。栈有后进先出的特点,栈中的数据大小与生存期都是确定的,缺乏灵活性,但是,存取速度比堆要快,仅次于CPU中的寄存器,另外栈中的数据是共享的。在Java中,所有的基本数据类型和引用变量(对象引用)都在栈中存储,栈中数据的生存空间一般在当前的scopes内,也就是“{}”的部分,比如:函数的参数值,局部变量等,是自动清除的。 - Heap堆 - Java中只有一个堆,被所有线程共享。堆中的数据没有先后顺序(逻辑上连续就好),堆中的数据不需要事先告诉编译器它的生存期,可以动态的分配内存的大小(动态的申请内存空间),也就是这样导致了存取速度慢。不再使用的数据由Java中的垃圾回收机制自动回收。在Java中由new创建出来的对象都是在堆中的,当垃圾回收机制检测到某对象未被引用时,则自动销毁该对象。 ------ - 基本的变量类型只有⼀块存储空间(分配在stack中), - 传递的时候直接是值传递 - 对数据进⾏操作,不影响原先的值 - 引⽤类型有两块存储空间 (⼀块在stack中,⼀块在heap中) - ⼀个对象在内存中会请求⼀块空间来保存数据,访问对象的时候不会直接是访问对象在内存中的数据,⽽是通过引⽤去访问 - 什么是引⽤??? 也是⼀种数据类型,保存的是内存的地址,引⽤是存储在 stack 栈空间⾥⾯ - 不同的引⽤可以指向同⼀个对象,⼀个对象可以有多个引⽤ > 下⾯代码在堆栈⾥⾯怎么分配的? ```java int a=1; boolean flag=false; Student s = new Student(); Person p = new Person(); ``` ![内存地址的引用](http://xk857.com/blog/2021/02/内存地址的引用.png) 引⽤后修改结果如何呢? - 因为字符串、数组或者对象是引⽤类型,所以修改后正在的数据都会发⽣变化 \ - 下⾯代码分别输出什么内容 ```java String a = "李四"; String b = "l love java"; System.out.println(a); System.out.println(b); b = a ; System.out.println(a); System.out.println(b); a = "李四开始学习多线程"; System.out.println(a); System.out.println(b); ``` ## ArrayList ### 基本使用 ```java private static void ArrayListDemo02() { ArrayList list = new ArrayList<>(); // 增 list.add("我是一个数据"); // 查 System.out.println(list.get(0)); // 改 list.set(0,"我是修改后的数据"); System.out.println(list.get(0)); // 删 list.remove(0); list.remove("我是修改后的数据"); // 集合大小 list.size(); // 清空元素 list.clear(); // 是否为空 list.isEmpty(); } ``` ### 集合的遍历 ```java private static void ArrayListFor() { ArrayList list = new ArrayList<>(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); System.out.println("=============常规for循环============="); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("=============增强for循环============="); for (String s : list) { System.out.println(s); } } ``` ### 使用集合模拟数据库存储对象 创建 User 用户对象 ```java /** * @author CV大魔王 * @Description 用户对象 * @date 2021/3/11 14:44 */ public class User { private String userName; private String pwd; private Integer age; private String realName; //get&set方法 } ``` 查询所有数据 demo02.dao.UserDao ```java public List findAll(){ ArrayList userList = new ArrayList<>(); User user = new User(); user.setUserName("zhangsan"); user.setPwd("123456"); user.setAge(18); user.setRealName("张三"); userList.add(user); user = new User(); user.setUserName("lisi"); user.setPwd("123456"); user.setAge(19); user.setRealName("李四"); userList.add(user); for (User u : userList) { System.out.println(u); } return userList; } ``` 引用 ```java public static void main(String[] args) { UserDao userDao = new UserDao(); userDao.findAll(); } ``` #### 优化 思考:如果是学籍成绩管理系统中使用,我们怎样去写呢? 优化一:创建类,模拟数据库存储数据 ```java package demo02.db; import demo02.pojo.User; import java.util.ArrayList; /** * @Description 模拟数据库存储数据 * @date 2021/3/11 15:00 * @author CV大魔王 */ public class Student { public static ArrayList userList = new ArrayList<>(); /** * 初始化用户数据 */ public static void initUserData() { User user = new User(); user.setUserName("zhangsan"); user.setPwd("123456"); user.setAge(18); user.setRealName("张三"); userList.add(user); user = new User(); user.setUserName("lisi"); user.setPwd("123456"); user.setAge(19); user.setRealName("李四"); userList.add(user); } } ``` dao层此时仅仅用来操作数据库 ```java /** * @author CV大魔王 * @Description 操作用户数据库 * @date 2021/3/11 14:46 */ public class UserDao { /** * 查询所有用户数据 * @return 用户集合对象 */ public List findAll(){ ArrayList userList = Student.userList; for (User u : userList) { System.out.println(u); } return userList; } } ``` 优化二:User实体类增加全参构造方法 demo02.pojo.User ```java public User(String userName, String pwd, Integer age, String realName) { this.userName = userName; this.pwd = pwd; this.age = age; this.realName = realName; } public User() { } ``` demo02.db.Student ```java /** * @Description 模拟数据库存储数据 * @date 2021/3/11 15:00 * @author CV大魔王 */ public class Student { public static ArrayList userList = new ArrayList<>(); /** * 初始化用户数据 */ public static void initUserData() { // User user = new User(); // user.setUserName("zhangsan"); // user.setPwd("123456"); // user.setAge(18); // user.setRealName("张三"); // userList.add(user); // // user = new User(); // user.setUserName("lisi"); // user.setPwd("123456"); // user.setAge(19); // user.setRealName("李四"); // userList.add(user); userList.add(new User("zhangsan","123456",18,"张三")); userList.add(new User("lisi","123456",18,"李四")); userList.add(new User("wangwu","123456",18,"王五")); userList.add(new User("zhaoliu","123456",18,"赵六")); userList.add(new User("huangqi","123456",18,"黄七")); } } ``` ### 延伸:常见面试题 1. ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是增加和删除慢 2. ArrayList默认大小和动态扩容机制? > JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0 > > 未指定集合容量,默认是0,若已经指定大小则集合大小为指定的; > 当集合第一次添加元素的时候,集合大小扩容为10 > ArrayList的元素个数大于其容量,扩容的大小= 原始大小+原始大小/2 ### 高阶-手写简单版ArrayList - 考察点:源码设计思想、代码编写规范 - 实战:设计一个简单的ArrayList - 注意:不要求和jdk源码一样,但尽量保证需求 #### 高阶-手写ArrayList(上) 包含构造函数(有参无惨)、add、==扩容机制== ```java import java.io.Serializable; /** * @author CV大魔王 * @Description ArrayList简单实现版 * @date 2021/3/14 10:18 */ public class MyArrayList implements Serializable { // 第一次扩容的容量 public static final int DEFAULT_CAPACITY = 10; // 用于初始化空的list public static final Object[] EMPTY_ELEMENT_DATA = {}; // 实际存储的元素 注:transient含义是不让此元素被序列化 transient Object[] elementData; // 实际list集合大小,从0开始 private int size; /** * 空参构造函数,将空对象赋值给elementData,elementData是用来实际存储的元素 */ public MyArrayList() { this.elementData = EMPTY_ELEMENT_DATA; } /** * 有参构造函数 * @param initialCapacity 初始容量 */ public MyArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENT_DATA; } else { // 如果参数不合规范就抛出异常IllegalArgumentException表示参数不合法。 throw new IllegalArgumentException("参数异常"); } } /** * 向数组添加内容 * 注:需要注意这里是怎样进行扩容的 * @param e 添加的内容 * @return 逻辑值,表示插入是否成功 */ public boolean add(Object e) { // 判断容量 ensureCapacityInternal(size+1); // 使用下标复制,尾部插入 elementData[size++] = e; return true; } /** * 计算容量+确保容量 * @param minCapacity */ private void ensureCapacityInternal(int minCapacity) { // 如果是初次扩容,则使用默认容量 if (elementData == EMPTY_ELEMENT_DATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } // 是否需要扩容,需要的最少容量大于现在数组长度则要扩容 if (minCapacity - elementData.length > 0) { // 获取到旧容量 int oldCapacity = elementData.length; // 动态扩容机制:旧容量 + 旧容量/2 int newCapacity = oldCapacity + (oldCapacity>>1); // 如果新容量 < 最小容量,则将最新容量赋值给新的容量 if (newCapacity - minCapacity < 0){ newCapacity = minCapacity; } // 创建新数组 Object[] objects = new Object[newCapacity]; //将旧的数组复制到新的数组里面 System.arraycopy(elementData,0,objects,0,elementData.length); // 修改引用 elementData = objects; } } } ``` #### 高阶-手写ArrayList(下) ArrayList 增删改方法完善 ```java import java.io.Serializable; /** * 注:为方便理解,所有并发判断的处理,均未实现 * @author CV大魔王 * @Description ArrayList简单实现版 * @date 2021/3/14 10:18 */ public class MyArrayList implements Serializable { // 第一次扩容的容量 public static final int DEFAULT_CAPACITY = 10; // 用于初始化空的list public static final Object[] EMPTY_ELEMENT_DATA = {}; // 实际存储的元素 注:transient含义是不让此元素被序列化 transient Object[] elementData; // 实际list集合大小,从0开始 private int size; // 使用这个字段,来判断当前集合类是否被并发修改,即迭代器并发修改的fail-fast机制 // private transient int modCount = 0; /** * 空参构造函数,将空对象赋值给elementData,elementData是用来实际存储的元素 */ public MyArrayList() { this.elementData = EMPTY_ELEMENT_DATA; } /** * 有参构造函数 * * @param initialCapacity 初始容量 */ public MyArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENT_DATA; } else { // 如果参数不合规范就抛出异常IllegalArgumentException表示参数不合法。 throw new IllegalArgumentException("参数异常"); } } /** * 向数组添加内容 * 注:需要注意这里是怎样进行扩容的 * * @param e 添加的内容 * @return 逻辑值,表示插入是否成功 */ public boolean add(Object e) { // 判断容量 ensureCapacityInternal(size + 1); // 使用下标复制,尾部插入 elementData[size++] = e; return true; } /** * 计算容量+确保容量 * * @param minCapacity */ private void ensureCapacityInternal(int minCapacity) { // 如果是初次扩容,则使用默认容量 if (elementData == EMPTY_ELEMENT_DATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } // 是否需要扩容,需要的最少容量大于现在数组长度则要扩容 if (minCapacity - elementData.length > 0) { // 获取到旧容量 int oldCapacity = elementData.length; // 动态扩容机制:旧容量 + 旧容量/2 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果新容量 < 最小容量,则将最新容量赋值给新的容量 if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } // 创建新数组 Object[] objects = new Object[newCapacity]; //将旧的数组复制到新的数组里面 System.arraycopy(elementData, 0, objects, 0, elementData.length); // 修改引用 elementData = objects; } } /** * 通过下标获取对象 * @param index 索引(用户通过索引获取对象) * @return 返回对象 */ public Object get(int index) { rangeCheck(index); return elementData[index]; } /** * 检查索引是否越界 * @param index 索引 */ private void rangeCheck(int index) { if (index > size || size < 0) { throw new IndexOutOfBoundsException("数组越界"); } } /** * 判断对象所在位置 * @param o 对象内容 * @return 对应位置索引 */ public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) { if (elementData[i] == null) { return i; } } } else { for (int i = 0; i < size; i++) { if (o.equals(elementData[i])) { return i; } } } return -1; } /** * 更新 * @param index 索引位置 * @param obj 更新后的值 * @return 旧的值 */ public Object set(int index,Object obj){ // 检查合法性 rangeCheck(index); // 获取旧的值 Object oldValue = elementData[index]; // 将新的值赋值 elementData[index] = obj; return oldValue; } /** * 根据索引删除元素 * 难点分析:元素移动 * @param index 索引 * @return 删除的元素的值 */ public Object remove(int index){ // 校验合法性 rangeCheck(index); // 获取旧的内容 Object oldVale = elementData[index]; // 计算要删除的位置后面有几个元素 int numMoved = size - index - 1; if (numMoved > 0) { // 1.原数组 2.原数据的起始位置 3.目标数组 4.目标数组的开始起始位置 5.要copy的数组的长度 System.arraycopy(elementData,index+1,elementData,index,numMoved); } // 将多出的位置置空,补充:置空后的对象,jvm垃圾回收机制,会自动进行回收。如果不进行置空,引用还在,可能会出现内存泄漏 elementData[--size] = null; return oldVale; } /** * 获取集合大小 * @return 集合大小 */ public int size(){ return size; } } ```