数据类型
| 数据类型 | 占用字节 | 位数 | 范围 |
|---|---|---|---|
| byte | 1字节 | 8位 | -128 ~ 127 |
| short | 2字节 | 16位 | -32768 ~ 32767 |
| int | 4字节 | 32位 | 约 ±21亿 |
| long | 8字节 | 64位 | 很大 |
| float | 4字节 | 32位 | 小数 |
| double | 8字节 | 64位 | 更高精度小数 |
| char | 2字节 | 16位 | 0 ~ 65535 |
| boolean | 理论上1位,但Java规范不规定大小(通常按1字节处理) | true/false |
算术运算符
隐式转换
- 取值范围:byte<short<int <long<float<double
- 什么时候转换:数据类型不一样,不能进行计算,需要转成一样的才能计算
- 转换规则1:取值范围小的,和取值范围大的进行计算,小的会先提升为大的,再进行计算
- 转换规则2:byte short char 三种类型的数据在运算的时候,都会直接提升为int,然后在进行计算
强制转换
如果把一个取值范围大的数值,赋值给取值范围小的变量,是不允许直接复制的,如果一定要转得加入强制转换
格式,目标数据类型 变量名=(目标数据类型)被强转的数据;
构造方法:创建对象的时候,虚拟机会自动调用
构造方法,作用是给成员变量进行初始化的
标准的JavaBean
类名需要见明知意
提供至少两个构造方法无参加全参,
成员方法:提供给每一个成员变更对应的set和get方法,如果还有其他行为,也得加上
一个对象的内存图
this的内存原理
作用:区分局部变量和成员变量
本质:所在方法调用这的地址值
创建String对象的两种方式
1、 直接赋值(省内存)
2、new 构造方法
new方式获取一个空字符串对象:空参构造;传递一个字符串,根据传递的内容在创建一个新的字符串对象
传递一个字符数组,根据字符数组内容在创建一个新的字符串对象
传递一个字节数组,根据字节数组内容在创建一个新的字符串对象
当使用双引号直接赋值时,系统会检查该字符串在串中是否存在
不存在:创建新的;存在:复用
字符串的比较
基本数据类型比较的是数据值
引用数据类型比较的是地址值
boolean equals方法(要比较的字符串) 完全一样的才true否则false booleanequalslgnoreCasse(要比较的字符串)忽略大小写的比较字符串原理
字符串拼接的底层原理
如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,会复用穿刺中的字符串
如果有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存
StringBuilder提高效率原理图
所有拼接的内容都会往SpringBuilder中放,不会创建无用的空间,节约内存
StringBuilder源码分析
集合
迭代器
迭代器在遍历集合的时候是不依赖索引的
迭代器需要掌握的三个方法
迭代器的细节:
如果当前位置没有元素,还要强行获取,汇报NoSuchElementException
迭代器遍历完毕,指针不会复位
循环中只能用一次next方法
迭代器遍历时,不能用集合的方法进行增加或者删除
List集合的遍历
迭代器遍历 在遍历中删除元素
列表迭代器 在遍历中添加元素,
增强for遍历 仅仅遍历
Lambda表达式 仅仅遍历
普通for 如果遍历的时候想操作索引
数据结构
是计算机底层存储、组织数据的方式
是指数据之间是以什么方式排列在一起的
常见的数据结构有:栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树
栈
特点后进先出,先进后出。
应用,栈内存中方法的运行时候进栈执行完毕出栈,从上往下一次出栈
队列
特点先进先出,后进后出
数据从后端进入,从前端出去的
数组
查询速度快:查询数据通过地址之和索引定位
删除效率低:要将原始数据删除,同时后面每个数据前移
添加效率低:添加位置后的每个数据后移,再添加元素
链表
链表中的节点是独立的对象,在内存中是不连续的,每个节点包含数据之和下一个节点的地址
创建一个链表,有一个头节点,
查询速度慢:无论查询哪个数据都要从头开始
链表增删快:只需要修改对应节点的地址值就行,动的很少
集合
集合(Collection)
│
├── 单列集合 Collection
│ │
│ ├── List
│ │ ├── ArrayList
│ │ └── LinkedList
│ │
│ └── Set
│ ├── HashSet
│ └── TreeSet
│
└── 双列集合 Map
│
├── HashMap
├── LinkedHashMap
├── TreeMap
└── ConcurrentHashMap
ArrayList底层原理
1,利用空参创建的集合,在底层创建一个默认长度为0的数组
2,在添加第一个元素时,底层会创建一个新的长度为10的数组
3、存满时,会扩容1.5倍
4、如果一次添加多个元素,1.5倍还放不下,则创建数组的长素以实际为准
ArrayList底层原理是通过数组(Object[])实现的,它内部维护了一个Object类型的数组element Date,用来存储集合中的元素,(同时通过size变量记录当前集合中实际存储元素的个数。)如果有则直接放入size位置;如果容量不足,则进行1.5倍扩容,创建新数组并复制旧数据。因此ArrayList查询效率高,因为底层是数组,可以通过下标进行快速访问,单数中间插入和删除效率较低,因为需要移动元素
比如执行ArrayList<String>list=newArrayList<>();底层:elementData[null,null,null,null,null]size=0当执行list.add("Java");调用ArrayList的add方法:publicbooleanadd(Ee){add(e,elementData,size);returntrue;}先把要添加的元素e,底层数组elementDate,当前元素数量size传进去 进入privatevoidadd(Ee,Object[]elementData,ints)e:要添加的数据 elementDate应用场景:普通业务 例如查询多的修改少单线程
CopyOnWriteArrayList
是线程安全版本的ArrayList,采用的是复制机制,在修改数据时复制一个新的数组,然后完成修改,最后替换原数组引用。它适合多线程环境下读多写少的场景,因为读操作无需加锁,性能高,但是写操作需要复制数组,会消耗更多内存
LinkedList集合
底层数据结构是双链表,查询慢,增删快,但是如果操作的是首位元素,速度也是极快的。
LinkedList本身多了很直接操作首尾元素的特有API
HashMap与ConcurrentHashMap
双列集合的特点:
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
-键+值成为”键值对(对象)“ 在Java中叫做“Entry对象”
特点:由键决定的:无序、不重复、无索引
底层原理是哈希表结构
核心:HashMap=数组+链表/红黑树+哈希算法
核心目的:通过key快速找到value
双列集合的遍历方式
1、键找值
Map<String,String>map=newHashMap<>();map.put("name","lihua");map.put("name","liii");map.put("age","18");map.put("sex","男");System.out.println(map.get("name"));System.out.println(map.get("age"));Stringresult=map.remove("name");System.out.println(result);System.out.println(map);Set<String>set=map.keySet();for(Stringkey:set){System.out.println(key);Stringvalue=map.get(key);System.out.println(key+"="+value);}Iterator<String>iterator=map.keySet().iterator();while(iterator.hasNext()){Stringkey=iterator.next();Stringvalue=map.get(key);System.out.println(key+"="+value);}System.out.println("--------------");set.forEach((s)->System.out.println(s));Collection<String>collection=map.values();Iterator<String>iterator2=collection.iterator();while(iterator2.hasNext()){Stringvalue=iterator2.next();System.out.println(value);}System.out.println("--------------");collection.forEach((s)->System.out.println(s));2、 键值对
Set<Map.Entry<String,String>>entries=map.entrySet();for(Map.Entry<String,String>entry:entries){Stringkey=entry.getKey();Stringvalue=entry.getValue();System.out.println(key+"="+value);}System.out.println("--------------");entries.forEach((s)->System.out.println(s));System.out.println("--------------");Iterator<Map.Entry<String,String>>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entry<String,String>entry=iterator.next();Stringkey=entry.getKey();Stringvalue=entry.getValue();System.out.println(key+"="+value);}3、Lambda表达式
Map<String,String>map2=newHashMap<>();map2.put("name","lihua");map2.put("age","18");map2.put("sex","男");map2.forEach(newBiConsumer<String,String>(){@Overridepublicvoidaccept(Stringkey,Stringvalue){System.out.println(key+"="+value);}});System.out.println("--------------");map2.forEach((key,value)->System.out.println(key+"="+value));}泛型
是JDK5引入的特性,在编译阶段约束操作的数据类型,并进行检查
泛型的格式:<数据类型>。泛型只能是引用数据类型
好处:统一数据类型
把运行时期的问题提前到编译期间,避免强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来
泛型类
泛型方法
方法中那个形参不确定时:
1、可以使用类名后面定义的泛型
2、在方法上申明定义自己的泛型
泛型接口
两种使用方式:
1、实现类给出具体的类型
2、实现类延续泛型,创建实现类对象时再确定类型
使用场景
定义类、方法、接口的时候,如果类型不确定,就可以定义泛型
如果类型不确定,但是能知道哪个是继承体系中的,可以使用泛型的通配符
异常
编译时异常用于提醒程序员写错,运行时异常是代码写错了已经
异常处理
异常中的常见方法
- Jvm默认的处理方式
把异常的名称,异常原因及异常出现的位置等信息输出在控制台
程序停止执行,下面代码不会再执行 - 自己处理(捕获异常)
try{可能出现异常的代码;}catch(异常类型 变量名){异常的处理代码;}如果try中没有到问题,怎么执行 会把try里面的代码执行完毕,不会执行catch里面的代码 如果try里面遇到多个问题,怎么执行 会写多个catch与之对应,父类异常写在下面 如果try中遇到的问题没有被捕获,怎么执行 相当于try...catch白写了,当前一场交给虚拟机处理 如果try中遇到问题,那么try下面的其他代码还会执行吗 不会执行了,try中遇到问题,直接跳到对应的catch如果没有对应的catch与之匹配,则会交给虚拟机处理- 抛出异常
throws:写在方法定义出,表示声明一个异常,告诉调用者,使用本方法可能会有哪些异常
编译时异常:必须要;运行时异常可以不写
throw:写在方法内,借助方法,手动抛出异常对象,交给调用者,方法中下面的代码不会再执行了
自定义异常
定义异常类
写继承关系编译或者运行异常
空参构造
带参构造
例如
**目的:**就是为了让控制台的报错信息更加见名知意
publicclassAgeFormatExceptionextendsRuntimeException{publicAgeFormatException(){}publicAgeFormatException(Stringmessage){super(message);}}publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);girlFriend gf=newgirlFriend();while(true){try{System.out.println("请输入姓名");Stringname=sc.nextLine();gf.setName(name);System.out.println("请输入年龄");Stringage=sc.nextLine();intageInt=Integer.parseInt(age);gf.setAge(ageInt);break;}catch(NumberFormatExceptione){System.out.println("年龄必须是数字");}catch(NameFormatExceptione){e.printStackTrace();}catch(ArithmeticExceptione){e.printStackTrace();}}System.out.println(gf);}线程状态