JAVA基础的类
1.字符串相关的类
String类
1.被final修饰的不可变的字符序列 不可被继承
2.内部是一个final []value 的字符数组,存储字符串
3.实现了 Serializable接口(实现对象序列化)comparable接口实现对象的比较
4.两种创建方式:1、 String通过字面量的方式进行赋值 String str = "a"
创建时相当于在常量池中开辟一个内存空间存储字符串
2、通过new实例化对象的方式,存储在堆中
5.因为String的final,所以每次字符串改变的时候其实是在字符串常量池中新开辟了一个空间存储一个字符串,而不是在原有的基础上去改(所以如果频繁对字符串修改的话,不建议使用String)
6.String 通过new 出来事实上是创建了2个对象,一个是堆空间中的实例对象,一个是常量池中的字符串,但事实上实例对象中的value值是指向常量池地址的
JVM常量池分为三种:
1. 静态常量池
2. 运行时常量池
3. 字符串常量池
String s1 = "a";
String s2 = "b";
String s3 = "a"+"b"; //拼接成的 ab 还是在常量池中
String s4 = s1+"b";//相当于在堆中new了一个新的对象
String s5 = "ab";
s4
System.out.println(s3==s5);//true
System.out.println(s4==s5);//false s4是指向堆内存中的地址的
System.out.println(s4.intern() == s5);//true
//intern()返回字符串在常量池中的地址
如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.
如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的地址,所以不会改变参数的值。
( 对象包括对象引用即地址和对象的内容)
a.传递值的数据类型:八种基本数据类型和String(这样理解可以,但是事实上String也是传递的地址,只是string对象和其他对象是不同的,string对象是不能被改变的,内容改变就会产生新对象。那么StringBuffer就可以了,但只是改变其内容。不能改变外部变量所指向的内存地址)。
b.传递地址值的数据类型:除String以外的所有复合数据类型,包括数组、类和接口
contains()//KMP算法
matches()
replace()//配合正则表达式来实现对字符串的控制
String的转换
char[]-->String //直接new
编码 String-->byte[] //使用getbytes("编码")
解码 byte[]-->String//使用new String(数组,“编码”)
Stringbuffer 和 StringBuilder
String Stringbuffer StringBuilder的异同
-
String: 不可变 的字符序列 char[]实现
-
Stringbuffer: 可变 的字符序列 char[]实现 线程安全 (底层方法都被Syncronized修饰) 效率低 char[]
-
StringBuilder: 可变 的字符序列 char[]实现 线程不安全 效率高
底层实现
创建时是创建一个char[] value = char [16]; 如果容量不够再次扩容,扩容一般是 length<<1 +2 (2倍+2) 但是我们在定义时是可以通过构造器指定容量的,new StringBuilder (int capti),避免多次扩容造成浪费。
//常用的方法
append();
delete(int start,int end);
intsert();
replace();
reverse();//反转
setcharat();
charat();
2.JDK8之前的日期API
DATE
java.util.date
两个构造器 new date()
new date
()(long 毫秒)
毫秒 可以从 System.currentTimeMillis
中获得(时间戳)
java.sql.date
long time = System.currentTimeMillis();//1605517141359
System.out.println(time);
Date date = new Date();
System.out.println(date.toString()); //Mon Nov 16 17:02:39 CST 2020
Date date1 = new Date(time);
System.out.println(date1); //Mon Nov 16 17:02:39 CST 2020
//sql的date转换
java.sql.Date date3 = new java.sql.Date(1605517141359L);//没有无参构造器
System.out.println(date3);//2020-11-16
java.sql.Date date4 = new java.sql.Date(date.getTime());
System.out.println(date4); //2020-11-16
SimpleDateFormat
格式化和解析
格式化:将Date转化为你想要的格式以字符串方式 format()
解析 :将字符串转化为Date parse()
格式patten 在SimpleDateFormat构造中自己定义
calendar
日历类 本身是一个抽象类 实例化要用其子类
get() set() add() getTime() setTime()
Calendar time =Calendar.getInstance();
Calendar time2 = new GregorianCalendar();
Date time1 = time.getTime();
System.out.println(time1);
3.JDK8之后的新日期API(现在项目开发中用的比较多)
java.time
localDate localTime localDateTime
//获取现有的时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();;
LocalDateTime localDateTime = LocalDateTime.now();
//设置时间 因为是不可变性(final) 所以原来的时间不改而是返回新的一个实例 (String)
localDate = LocalDate.of(2077,1,2);
localTime = LocalTime.of(2077,1,2);
localDateTime = LocalDateTime.of(2077,1,2,2,2,2);
//getXXX 获取xx值
//withXXX 修改xx 但是会返回新的时间(不可见性) 相当于calendar的get
//构造方法私有的 所以构造的时候只能用静态调用
private LocalDateTime(LocalDate date, LocalTime time) {
this.date = date;
this.time = time;
}
Instant
时间上的一个瞬时点
// 构造器被私有化
Instant now = Instant.now();
//设置东八区
now.atOffset(ZoneOffset.ofHours(8));
//相当于Date的getTime();获取毫秒数
now.toEpochMilli();
System.out.println(date.getTime());//1605533292406
System.out.println(now.toEpochMilli())//1605533292401
4.JAVA比较器
Comparable
自然排序
像String、包装类等都实现了comparable接口 重写了compareTo()
方法
自定义类来排序就是在创建类的时候继承Comparable接口,并重写compareTo方法
public class Person implements Comparable {
private String name;
@Override//重写compareto方法
public int compareTo(Object o) {
if(o instanceof Person ){
Person p =(Person) o;
return this.name.compareTo(p.name);
}
else return -1;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Comparator
定制排序
比如我们在类中实现了comparable接口采用的是降序排序,但是我们现在在处理的时候打算用降序来排序,总不能修改源码吧,那么我们就可以用外接的comparator接口来实现降序排序。
Arrays.sort(Collection , Comparator)第二个参数就是接收的比较器
相当于一次性定义一个比较器 我们可以使用局部类来使用
String[] s = {"sdf","AA","BB"};
Arrays.sort(s, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(s));
5.System类
构造器是private的,但里面的属性和方法大多是静态的。
err()
in()
系统输入
out()
系统输出
gc()
垃圾回收
getProerty()
系统属性
6.MATH类
数学类
ceil() 返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型。 |
floor() 返回小于等于(<=)给定参数的最大整数 。 |
rint() 返回与参数最接近的整数。返回类型为double。 |
round() 它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11。 |
min() 返回两个参数中的最小值。 |
max() 返回两个参数中的最大值。 |
exp() 返回自然数底数e的参数次方。 |
log() 返回参数的自然数底数的对数值。 |
pow() 返回第一个参数的第二个参数次方。 |
sqrt() 求参数的算术平方根。 |
sin() 求指定double类型参数的正弦值。 |
cos() 求指定double类型参数的余弦值。 |
tan() 求指定double类型参数的正切值。 |
asin() 求指定double类型参数的反正弦值。 |
acos() 求指定double类型参数的反余弦值。 |
atan() 求指定double类型参数的反正切值。 |
atan2() 将笛卡尔坐标转换为极坐标,并返回极坐标的角度值。 |
toDegrees() 将参数转化为角度。 |
toRadians() 将角度转换为弧度。 |
random() 返回一个随机数。 |
7.BigDecimal和BigInteger
-
BigInteger
因为 long类型 的整数范围是有限的,在 Java中 引入了专门用来进行大数操作的一个类 —— BigInteger类。 -
BigDecimal
大小数精度缺失精度丢失
注解和枚举类
枚举类
定义一组有关系的常量时建议用枚举类
//直接用enum 定义
//定义的类有限个数 且为final不能被修改
//构造器私有化 静态定义
enum Season{
SPRING,
SUMMER("夏天","夏日炎炎"),
FALL,
WINTER;
private Season() {
}
private Season(String name, String src) {//构造器私有化
}
}
//枚举类实现接口后 里面的`SPRING`其实也可以实现接口,可以把它当成一个内部类
interface show{
public void show();
}
enum Season implements show{
SPRING,
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("这是夏天");
}
},
FALL,
WINTER;
private Season() {
}
private Season(String name, String src) {
}
@Override
public void show() {
System.out.println("这是一个季节");
}
}
public class Main {
@Test
public void test(){
Season[] a= Season.values();
Season.FALL.show();//这是一个季节
Season.SUMMER.show();//这是夏天
}
}
注解
常用的注解
@Overwride 重写
@Deprecated 过时
@SuppressWarings("参数")
....
生成文档时需要用到的注解
@author 作者
@verrsion 版本
@see 主题
@since 那个版本开始
@param 参数
@return
@exception 异常
元注解(重要)
用来注解自定义注解的注解
@Target 指明注解的作用域
@Retention 指明注解的生命周期 SOURCE < CLASS < RUNTIME
@Documented 是否将注解生成到文档中
@Inhereted 子类可以继承父类的注解
自定义注解
使用反射获取注解才有意义
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
//注解的参数:参数类型+参数名()
String name() default "11";
int age();//如果没有默认值使用的时候必须赋值
Sring value(); //赋值的时候可以省略
}
反射
反射很重要!我们的动态代理 SpingBoot中注解…都需要用到反射
反射在我们理解Spring AOP思想很重要哦!
我们的单例模式也可以使用反射来破解
Class
- class本身也是一个类
- 只能有系统创建
- 一个加载的类在JVM中只有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成的
- 加载calss时,其元数据、常量池、字段、方法都放在方法区
获得Class类实例的方式
Person p = new Person();
//方式一
Class c1 = Person.class;
//方式二
Class c2 = p.getClass();
//方式三
Class c3 = Class.forName("Student");
//方式四
Class c4 = Integer.TYPE;
//方式五 获得父类
Class c5 = c3.getSuperclass();
类的加载过程
- 加载 :JVM将.class字节码文件加载到内存中,并将这些数据(静态变量、静态方法块、常量池等存放在方法区中),并在堆中生成Class对象代表这个类
- 校验: 确保加载的类符合JVM安全规范
- 准备: 正式为类的变量(static变量)设置初始值(例如 Integer 设为0)并分配内存空间
- 解析:虚拟机常量池中的符号引用替换为直接引用 地址引用 (比如我们在定义的时候引了另一个类,但是我们并不知道他是啥,就先给他一个符号,解析的时候再来获得他的内存地址改为直接引用)
不懂符号引用和直接引用的可以参考这个博客:https://www.cnblogs.com/shinubi/articles/6116993.html - 初始化: 执行类构造方法
<clinit>()
,自动收集类变量的赋值,和执行static静态代码块
什么时候会发生类的初始化
这是个有趣的问题哦
注意加载子类的时候父类也会被加载
类的主动引用
public class Test {
static{
System.out.println("main 类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
Son son = new Son();//主动创建实例
Class.forName("Son");//通过反射创建实例
}
}
class Father{
public static int p =0;
static{
System.out.println("父被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
}
}
main 类被加载
父被加载
子类被加载
类的被动引用
public class Test {
static{
System.out.println("main 类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(Son.p);//通过子类调用父类的静态变量/
System.out.println(Father.p);//通过子类调用父类的静态变量
Son[] sons = new Son[5];//
}
}
class Father{
public static int p =0;
static{
System.out.println("父被加载");
}
}
class Son extends Father{
public static int m =2;
public static final int x =2;//常量都不会被调用
static {
System.out.println("子类被加载");
}
}
类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
System.out.println(Class.forName("java.lang.Object").getClassLoader());
获取类的完整结构(反射)
Class c1 = Class.forName("Son");
Field[] files = c1.getFields();//获得父类和子类的public的属性
for (Field field : files) {
System.out.println(field);
}
Field[] files1 = c1.getDeclaredFields();//获得本类的所有属性
for (Field field : files1) {
System.out.println(field);
}
c1.getField("sex");//获取指定的属性
Constructor[] Constructors = c1.getDeclaredConstructors();//获得父类和子类的publi构造器
Constructor[] Constructors1 = c1.getConstructors();//获得本类的所有构造器
c1.getConstructor();//获取指定的构造器
c1.getMethods();//获得父类和子类的publi方法
c1.getDeclaredMethods();//获得本类的所有方法
c1.getDeclaredMethod("get",Integer.class);//获取指定的方法
Class son = Class.forName("Son");
Annotation[] annotations = son.getAnnotations();//获取类所有的注解 但不能获取到内部的
MyTable annotation = (MyTable) son.getAnnotation(MyTable.class);
Field age = son.getDeclaredField("age");
MyFiled annotation1 = age.getAnnotation(MyFiled.class);//获取属性的注解
System.out.println(annotation1.colnum());//获取值
System.out.println(annotation1.type());
通过放射获取对象的构造器、方法、属性 注解
Class c1 = Class.forName("Son");
//方式一 反射 本质调用无参构造器实例化对象
Son son = (Son) c1.newInstance();
System.out.println(son);
//方式二 获取构造函数调用
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, boolean.class);
Son bh = (Son) constructor.newInstance("bh", 11, true);
System.out.println(bh);
//通过反射获取方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(bh,"bh");//激活方法
System.out.println(bh);
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//关闭程序的安全监测,可以操作private的属性
name.set(bh,"bs");
System.out.println(bh);
性能对比
使用new 3ms
Son son = new Son();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
son.getName();
}
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
使用放射 2946ms
Class c1 =Son.class;
Son son = (Son) c1.newInstance();
Method getName = c1.getDeclaredMethod("getName");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(son,null);
}
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
反射操作泛型
Class son = Class.forName("Son");
Method test = son.getDeclaredMethod("test", Map.class);
Type[] genericParameterTypes = test.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
System.out.println(genericParameterType);
}
集合
Collection接口方法
add();
addAll();//集合
clear();
isEmtpy();
contains();//需要调用对象的equal()
containsAll();
remove();
removeAll();
retainsAll();
equal();
hashcode();//返回哈希值
toArray();//返回数组
iterator();
Iterator迭代器 接口
hasNext()
next()
remove()//不同于集合调用remove()
@Test
public void test(){
Collection a = new ArrayList<Integer>();
a.add(1);
a.add(1);
a.add(1);
a.add(1);
Iterator iterator = a.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
Collection子接口:List
ArrayList LinkedList Vector
-
ArrayList: 效率高 底层用
Object[] elementData
实现 线程不安全 源码分析: elementData在创建时默认为{},在add后创建长度为10 的 list,当长度不够时grow()扩容 1.5倍 -
LinkedList: 双向链表实现的 增删容易
源码分析:
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
-
Vector: 效率低 底层用Object[]实现 线程安全 底层使用synchronized效率极低
源码分析: 创建长度默认为10的数组,每次扩容一倍
Collection子接口:Set
底层还是数j组
1.无序性: 不等于随机性 ,在底层数组的存储并非按照数组下标索引存储,而是按照元素的哈希值来存储
2.不可重复性: 调用元素的equal()方法和hashcode()方法来实行set中只有一个
在set中添加的元素必须重写equal和hashcode方法
HashSet LinkedHashSet TreeSet
HashSet : 无序的 不重复的元素 线程不安全的 可以存储null
LinkedHashSet : 事实上也是无序的,底层也是用数组存储的,但是在添加的时候使用尾插法,创建了一个链表,来实现遍历的时候比较快
TreeSet : 可以为插入的对象排序
根据什么排序?
-
一个是创建类对象的时候继承Comparable借口
-
二是在创建TreeSet的时候传入一个comparator的参数
new TreeSet(Comparator o);
Map接口
hashtable hashMap TreeMap ConcurrenthashMap
ConcurrenthashMap CAS comparaAndSwag 保证线程安全
|----Map :双列数据 存储Key–Value的数据 底层(数组+链表+红黑树)
|----hashtable 古老的实现类 线程安全 效率低不能存储null
|-----Properties常用来处理配置文件,key和value都是String类型 (这个我们在设置JDBC的时候是有设置过的)
|----hashMap : 最常用的实现类 线程不安全,效率高 可以存储null
|----LinkedHashMap :添加了2个指针,来实现按照插入的顺序来遍历
|----TreeMap : 通过Key的值来构成红黑树实现排序,当然对象要实现2种排序的接口
HashMap<Integer, Integer> a = new HashMap<>();
//实现原理: 在创建Map时创建一个Node[];没有定义大小
a.put(1,1);
/* 在放入数据的时候,存入的是一个Entry类型的(k-v),然后定义为初始的16大小的数组
计算key1的hashcode 并经过散列函数后如果在数组中并没有数据 则直接存入
否则 判断这个位置的链表上的每个key的hashcode是否相同的 没有则 存入这个位置
有的话调用equals()判断是否相同
是的话 替换 否则存入这个数据 (尾插法)
*/
//如果数组中某个位置的链表长度超过8 且数组的长度小于 64 的话改用红黑树存储而不是用链表存储 否则就扩容
//扩容条件 如果一个位置有值得话,判断负载因子*实际容量(阈值)
负载系数:DEFAULT_LOAD_FACTOR 0.75f
阈值: threshold =EFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR
红黑树: TREEIFY_THRESHOLD 8
//LinkHashMap实现原理
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
//链表的头和尾
transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
Map的方法
增 put();
删 remove(); clear();
查 get(key); containsKey(key); containsValue(v); size(); isEmpty();
遍历
//键值Set
Set<Integer> integers = a.keySet();
Iterator<Integer> iterator = integers.iterator();
//Value的Collection
Collection<Integer> values = a.values();
Iterator<Integer> iterator1 = values.iterator();
//键值对的Entry
Set<Map.Entry<Integer, Integer>> entries = a.entrySet();
Iterator<Map.Entry<Integer, Integer>> iterator2 = entries.iterator();
while (iterator2.hasNext()){
Map.Entry entry=iterator2.next();
System.out.print(entry.getValue());
System.out.println(entry.getKey());
}
改 put()
Map 的底层原理
//初始化hashmap的时候 先创建一个长度为0的Node类型的数组
//在执行put()操作的时候会初始化扩容 长度16
//put的时候计算2次Key的hashcode 以保证散列
//找到数组的位置,对键值对进行操作
扩容
// 如果数组的某个地方有位置的时候,判断一下数组的实际长度是否达到扩容的要求,若是就扩容
Collections工具类
reverse(List);
shuffle(List);
sort(List,Compararor);
swag(List,int,int);
max(Collection);
frequency(Collection,Object);//好用 出现几次
copy(List,List);
XXX xx = synchronizedXXX(); //返回线程安全的xxx
泛型
在定义类或者接口的时候使用泛型,可以保证存入多种类型的数据,只要在实例化的时候规定一下泛型就行,
例如在Collection<>
里面,我们可以存各种对象类型的值,我们用泛型定义的话,就可以保证存入的数据是同一个对象类型的。
自定义泛型
通配符 < <?extends xx> <?super xx>
IO流
File类
文件或者文件目录
//构造器1
File a = new File("1.txt");
File b = new File("D:\\1.txt");
//构造器2
File c = new File("E:\\垃圾","1.txt");
//构造器3
File d = new File(c,"1.txt");
a.getAbsoluteFile();//绝对路径
a.getPath();//路径
a.getName();//名字
a.getParent();//母文件
a.length();//多大
a.lastModified();//最后一次修改时间 (毫秒数)
String[] list = e.list();//查看文件下的目录
File[] list1 = e.listFiles();
for(String s: list){
System.out.println(s);
}
a.renameTo(new File("2.txt"));//移动加改名
a.isFile();//是否是文件
a.isDirectory();//是否是文件夹
a.canRead();//可读?
a.canWrite();//可写?
a.exists();//存在
a.isHidden();//隐藏?
File f = new File("3.txt");
f.createNewFile(); //创建文件
File g = new File("E:\\马云\\test");
g.mkdir();//穿件文件夹
File h = new File("E:\\马云\\test\\sdf\\dfsdf\\sdf");
System.out.println(h.mkdir());.//false 创建文件夹,但是只创建母目录存在的
System.out.println(h.mkdirs());//true 创建多个文件夹
f.delete();//删除文件或者文件夹
@Test
public void test() throws IOException {
File g = new File("E:\\马云\\test");
//删除指定文件夹 如果文件夹有子文件就不能删除
deleteDi(g);
}
public static void deleteDi(File a){
if(a.isFile())
a.delete();//出口
if(a.isDirectory()){
File[] files = a.listFiles();
for (File file : files) {
deleteDi(file);//递归
}
}
a.delete();//上面的递归完 代表已经没有子文件了
}
IO流
抽象基类
字符流 | 字节流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutStream | Writer |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cmhnDccv-1615901156348)(C:\Users\77308\AppData\Roaming\Typora\typora-user-images\image-20201123144842913.png)]
字符流读入(节点流)
字符流不能处理图片视频这样的字节流数据
//filereader 读取文件夹内容
//
public void test() {
File f = new File("2.txt");
FileReader fr =null;
try {
fr = new FileReader(f);
char data;
//读入操作1 read没有传值 返回的是读取的字符
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
//读入操作2 read传了字符数组的话,返回的是数组的长度
char[] cbuf = new char[5];
int len;
while ((len = fr.read(cbuf)) != -1) {
for (int i = 0; i < len; i++) {
System.out.print(cbuf[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fr!=null)
fr.close();//一定要关闭 因为java的垃圾处理机制是无法处理物理上的链接的,比如数据库、socket
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流写出(节点流)
@Test
public void fileWire() throws IOException {
File f = new File("3.txt");
FileWriter fw = null;
try {
fw =new FileWriter(f,false);//false 为覆盖 true为 追加
fw.write("你是傻逼");
fw.write(char[] str, int begin,int end);//用来读取写去的时候用
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fw!=null)
fw.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
字节流输入输出(节点流)
@Test
public void fileWire() throws IOException {
File f = new File("4.png");
File f1 = new File("5.png");
FileInputStream fi = new FileInputStream(f);
FileOutputStream fo = new FileOutputStream(f1);
try {
byte[] buf = new byte[10];
int len;
while((len = fi.read(buf))!=-1){
fo.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fi.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
fo.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
处理流之缓冲流 写入写出
大大加快读写的速度
原因:内部提供了一个缓冲区 实际和我们写的时候的 char或者byte数组差不多,,优化应该是在写的时候
@Test
public void buffRW() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//创建文件实例
File f1 = new File("4.png");
File f2 = new File("6.png");
//创建处理流缓冲包装节点流实例
bis = new BufferedInputStream(new FileInputStream(f1));
bos = new BufferedOutputStream(new FileOutputStream(f2));
//读写数据
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流 (只要把外面的处理流关闭,包裹的基本节点流也会关闭)
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流
@Test
public void buffRW() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建字节流 文件
br = new BufferedReader(new FileReader(new File("2.txt")));
bw = new BufferedWriter(new FileWriter(new File("1.txt")));
//方式1 读写数据
char[] buffer = new char[1024];
int len;
while ((len = br.read(buffer)) != -1) {
bw.write(buffer, 0, len);
bw.flush();//刷新缓存区
}
//方式2
String data;
while ((data = br.readLine()) != null) {
bw.write(data);//不换行的
bw.newLine();
bw.flush();//刷新缓存区
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流 (只要把外面的处理流关闭,包裹的基本节点流也会关闭)
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
处理流之转换流
-
InputStreamReader :将字节流的输入转化为字符流写入到内存中 解码
-
OutputStreamWriter : 将字符流的输出转化为字符流写入到磁盘中 编码
@Test
public void iNPutSt() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
isr = new InputStreamReader(new FileInputStream("1.txt"), "utf-8");
osw = new OutputStreamWriter(new FileOutputStream("2.txt", false), "gbk");
char[] buffer = new char[1024];//读到的转化为字符流,所以用char
int len;
while ((len = isr.read(buffer)) != -1) {
osw.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (osw != null)
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (isr != null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
标准的输出、入流
System.in
System.out
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
打印流
printStream
printWriter
FileOutputStream fos = new FileOutputStream("5.txt");
PrintStream ps = new PrintStream(fos, true);
System.setOut(ps);
System.out.println();
数据流
-
DataInputStream
-
DataOutoutStream
用于存储基本数据类型的变量或者字符串
@Test
public void iNPutSt() throws FileNotFoundException {
DataInputStream dis = null;
DataOutputStream ois = null;
try {
dis = new DataInputStream(new FileInputStream("1.txt"));
ois = new DataOutputStream(new FileOutputStream("1.txt"));
ois.writeUTF("陈总");
ois.flush();//刷新缓存区
ois.writeInt(11);
ois.flush();
ois.writeChars("sdfsdfs\0");
ois.flush();
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
char c;
while ((c = dis.readChar()) != '\0')
System.out.println(c);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null)
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象流
ObjectInputStream
ObjectOotputStream
public void iNPutSt() throws FileNotFoundException {
ObjectInputStream dis = null;
ObjectOutputStream ois = null;
try {
ois = new ObjectOutputStream(new FileOutputStream(new File("1.dat")));
//写对象操作 序列化
ois.writeObject(new String("sdfsdfsadf"));
ois.flush();
dis = new ObjectInputStream(new FileInputStream(new File("1.dat")));
//反序列化 读
Object o = dis.readObject();
String str = (String) o;
System.out.println(str);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (dis != null)
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象的序列化
将对象序列化为二进制文件,使对象能够持久化,或者在网络上传输。传输后只需要反序列化就可以还原回对象了。
1.必须实现Serializable
接口,必须定义一个全局变量来唯一标识serialVersionUID
这个对象
2.保证类内部所有的属性可序列化的(如果有个类属性 Student s 没有实现序列化)
3.被static
和transient
修饰的属性不能被序列化
public class Person implements Comparable , Serializable {
private String name;
private static long serialVersionUID = 465464646L; }
随机存储文件流
RandomAccessFile dis = null;
RandomAccessFile ois = null;
try {
ois = new RandomAccessFile(new File("1.txt"),"rw");
//写对象操作 序列化 写的时候是覆盖但是以前的不删除
ois.write("sdfsdf".getBytes());
ois.seek(3);//从哪里开始读写
dis = new RandomAccessFile(new File("1.txt"),"rw");//这个和节点流同一地位
//反序列化 读
byte[] buffer = new byte[1024];
int len;
while ((len = dis.read(buffer)) != -1) {
for (int i = 0; i <len ; i++) {
System.out.println((char)buffer[i]);
}
}
NIO2中的Path Paths Files的使用
网络编程
IP
ip 唯一定位一台网络上的计算机
ABCD IP地址
A类地址
⑴ A类地址第1字节为网络地址,其它3个字节为主机地址。
⑵ A类地址范围:1.0.0.1—126.155.255.254
⑶ A类地址中的私有地址和保留地址:
① 10.X.X.X是私有地址(所谓的私有地址就是在互联网上不使用,而被用在局域网络中的地址)。
② 127.X.X.X是保留地址,用做循环测试用的。
B类地址
⑴ B类地址第1字节和第2字节为网络地址,其它2个字节为主机地址。
⑵ B类地址范围:128.0.0.1—191.255.255.254。
⑶ B类地址的私有地址和保留地址
① 172.16.0.0—172.31.255.255是私有地址
② 169.254.X.X是保留地址。如果你的IP地址是自动获取IP地址,而你在网络上又没有找到可用的DHCP服务器。就会得到其中一个IP。
C类地址
⑴ C类地址第1字节、第2字节和第3个字节为网络地址,第4个个字节为主机地址。另外第1个字节的前三位固定为110。
⑵ C类地址范围:192.0.0.1—223.255.255.254。
⑶ C类地址中的私有地址:
192.168.X.X是私有地址。
D类地址
⑴ D类地址不分网络地址和主机地址,它的第1个字节的前四位固定为1110。
⑵ D类地址范围:224.0.0.1—239.255.255.254
E类地址
⑴ E类地址也不分网络地址和主机地址,它的第1个字节的前五位固定为11110。
⑵ E类地址范围:240.0.0.1—255.255.255.254
InetAddress inet = InetAddress.getByName("127.0.0.1");
System.out.println(inet.getHostName());
InetAddress inet1 = InetAddress.getByName("www.baidu.com"); //ip类
System.out.println(inet1);
端口
端口表示计算机上的一个程序的进程。
-
不同的进程有不同的端口,用来区分程序
-
0~65536 *2
-
TCP UDP 分别占有这个个范围,相同不会冲突
-
端口的分类
-
公有的端口 1~1023
- HTTP 80
- HTTPS 443
- FTP 21
- TENLENT 23
-
程序注册私有的端口 1024 ~49151
- Tomcat 8080
- MySql 3306
- Oracle 1521
-
动态端口 私有端口49151~ 65535
-
netstat -ano //查看所有的端口
netstat -ano|findstr "443" //查看具体的端口
InetSocketAddress inet = new InetSocketAddress("127.0.0.1", 80);
System.out.println(inet.getAddress());
System.out.println(inet.getHostName());
System.out.println(inet.getPort());
通信协议
OSI七层模型 | TCP/IP模型 | |
---|---|---|
应用层 | ||
表示层 | 应用层 | HTTP Telnet FTP DNS SMTP |
会话层 | ||
传输层 | 传输层 | TCP UDP |
网络层 | 网络层 | IP IP RIP |
数据链路层 | 数据链路层 | ARP PARP IEEE802.3 |
物理层 | 物理层 |
tcp udp 对比
TCP :打电话
- 连接 稳定
- 三次握手 四次挥手 ABA ABBA
- 客户端 服务器端
- 传输完成 释放连接 效率低
UDP : 发短信
- 不连接 不稳定
- 没有服务端客户端的概念
- 只管发送一个包就完事
TCP服务器 客户端连接
服务器
- 创建一个serverSocket来接受客户端的信息,参数是端口
- accept持续监听是否有人来连接
- socket.getInputStream()用流的方式来处理接受的信息
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpService {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//创建一个serverSocket来接受客户端的信息,参数是端口
serverSocket = new ServerSocket(9999);
while (true) {
//持续监听是否有人来连接
socket = serverSocket.accept();
//用流的方式来处理接受的信息
is = socket.getInputStream();//socket可以返回流
baos = new ByteArrayOutputStream();//管道流
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer))!=-1) {
baos.write(buffer,0,len);
}
System.out.println(baos);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(baos!=null)
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(serverSocket!=null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端
- 找到服务器的ip 用InetAddress类装
- 创建一个套接字Socket(ip,port)来连接
- 使用IO流来发送socket.getOutputStream();
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class Tcpcline {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//找到服务器的ip 用inet类装
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 9999;
//创建一个套接字来连接
socket = new Socket(ip,port);
os = socket.getOutputStream();
os.write("我是你的爹".getBytes());//字节流
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os!=null)
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP
发送端
public static void main(String[] args) throws Exception{
DatagramSocket socket =new DatagramSocket(); //数据包socket
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 9090;
String s =new String("我是你爹");
DatagramPacket dap = new DatagramPacket(s.getBytes(),0,s.length(),ip,port);
socket.send(dap);//发送包
socket.close();
}
接受端
public static void main(String[] args) throws Exception {
DatagramSocket socket =new DatagramSocket(9090);//数据包socket
byte[] buffer = new byte[2024];
DatagramPacket dap = new DatagramPacket(buffer,0,buffer.length);
socket.receive(dap); //阻塞接受
System.out.println(dap.getAddress().getHostName());
System.out.println(new String(dap.getData(),0,dap.getLength()));
socket.close();
}
URL
统一资源定位符
//通过url下载
URL url = new URL("https://pic.xiami.net/images/common/uploadpic/36/1606296867036.jpg?x-oss-process=image/crop,y_30,h_360/quality,q_80/format,jpg");
//连接到这个资源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream ins = urlConnection.getInputStream();
FileOutputStream fos =new FileOutputStream("x.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = ins.read(buffer))!=-1 ){
fos.write(buffer,0,len);
}
fos.close();
ins.close();
urlConnection.disconnect();
多线程
三种创建方式
-
继承Tread类
public class TestThread extends Thread{ @Override public void run(){ for (int i = 0; i < 20 ; i++) { System.out.println("线程"+i); } } public static void main(String[] args) { TestThread testThread = new TestThread(); testThread.start(); for (int i = 0; i < 1000; i++) { System.out.println(i); } } }
-
继承Runable 接口 静态代理模式 函数式接口
public class TestThread implements Runnable { @Override public void run(){ for (int i = 0; i < 20 ; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } public static void main(String[] args) { //创建线程 TestThread t1 = new TestThread(); new Thread(t1,"线程1").start(); new Thread(t1,"线程2").start(); } }
-
继承Callable接口
public class TestThread implements Callable<Boolean> {
@Override
public Boolean call(){
for (int i = 0; i < 20 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+" "+i);
}
return true;
}
public static void main(String[] args) throws Exception {
//创建线程
TestThread t1 = new TestThread();
TestThread t2 = new TestThread();
TestThread t3 = new TestThread();
//开启服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdown();
}
}
使用Lamda表达式实现线程
前提是函数式接口
new Thread(()->{
for (int i = 0; i < 20 ; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}).start();
}
线程的方法
-
stop()线程停止 一般在线程体设置一个标志位,自己来是线程停止,比较安全
-
sleep(时间) 使线程睡眠 不会释放锁
-
yield() 礼让线程同级别
-
join()线程重新执行 且执行到完
线程的状态
Thread.State.BLOCKED
Thread.State.TERMINATED
Thread.State.NEW
Thread.State.TIMED_WAITING
Thread.State.RUNNABLE
public static void main(String[] args) throws InterruptedException {
//创建线程
Thread t = new Thread(()->{
for (int i = 0; i <5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///");
});
//观察状态
Thread.State state = t.getState();
System.out.println(state);
//观察启动后
t.start();
System.out.println(t.getState());
while(state!=Thread.State.TERMINATED){
Thread.sleep(100);
state=t.getState();
System.out.println(state);
}
}
}
线程的优先级
1~10
优先级高只是运行的时候线执行的概率加大了,但是不一定优先级高就一定被先执行
性能倒置: 线程优先级高的等待性能优先级低的线程对象的锁
public static void main(String[] args) throws InterruptedException {
//创建线程
System.out.println(Thread.currentThread().getName()+" >"+Thread.currentThread().getPriority());
Thread t1 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+" >"+Thread.currentThread().getPriority());
});
Thread t2 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+" >"+Thread.currentThread().getPriority());
});
Thread t3 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+" >"+Thread.currentThread().getPriority());
});
t1.setPriority(10);//设置优先级
t2.setPriority(5);
t3.setPriority(4);
t1.start();
t2.start();
t3.start();
}
守护线程 daemon
gc 垃圾回收线程就是守护线程
t.setDaemon(true);//设置为守护线程 默认为用户线程
线程同步
多个线程操作同一个资源导致并发的冲突,我们就需要synchroniced
锁来是线程同步
线程不安全的实例
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
Acount zx = new Acount("张鑫", 100);
Thread thread = new Thread(new Bank(zx, 50), "zx");
Thread thread1 = new Thread(new Bank(zx, 100), "yq");
System.out.println(zx.money);
thread.start();
thread1.start();
}
}
class Acount {
String name;
int money;
public Acount(String name, int money) {
this.name = name;
this.money = money;
}
}
class Bank implements Runnable {
Acount acount;
int needmonney;
public int getMoney(Acount acount, int needmonney) {
acount.money -= needmonney;
return acount.money;
}
public Bank(Acount acount, int needmonney) {
this.acount = acount;
this.needmonney = needmonney;
}
@Override
public void run() {
if (acount.money < needmonney) {
System.out.println("没钱了");
} else {
try {
Thread.sleep(1000);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
getMoney(acount, needmonney);
System.out.println(Thread.currentThread().getName() + "取了" + needmonney);
System.out.println("卡里的余额为: "+acount.money);
}
}
}
同步方法及同步块
Synchronized
https://blog.csdn.net/qq_40409115/article/details/80205676?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
//和上面的代码一样,加了acount锁之后就不会出现线程冲突了 这是同步块
//这边如果我们锁run方法的话,事实上是不能实现线程同步的 如果是继承Thread类的话,她实例的对象本身就带锁。别的线程也不能执行他的run方法,所以带不带锁问题也不大。(等下次好好研究一下)
@Override
public void run() {
synchronized (acount){
if (acount.money < needmonney) {
System.out.println("没钱了");
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
getMoney(acount, needmonney);
System.out.println(Thread.currentThread().getName() + "取了" + needmonney);
System.out.println("卡里的余额为: "+acount.money);
}
}}
}
死锁
四大条件: 互斥 请求保持 不可剥夺的 循环等待
LOCK
可重入锁 ReentrantLock
Synchronized 和 lock 的区别
-
lock是显示锁需要手动开关 Synchronized 是隐式锁,出了作用域就没用了
-
lock只锁代码块, Synchronized 锁代码块和方法
-
lock性能更好
import java.util.concurrent.locks.ReentrantLock;
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
Lock lock = new Lock();
new Thread(lock).start();
new Thread(lock).start();
new Thread(lock).start();
}
}
class Lock implements Runnable{
int ticket = 10;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if(ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticket--);
}
else{
break;
}
} finally {
lock.unlock();
}
}
}
}
线程间通信
wait() notify()的使用
管程法 信号灯法
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
SynContainer synContainer = new SynContainer();
new Thread(new Product(synContainer)).start();
new Thread(new Consumer(synContainer)).start();
}
}
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
class SynContainer {
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void push(Chicken chicken) {
if (count == chickens.length) {
//通知消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
//通知消费者可以消费了
this.notifyAll();
}
public synchronized Chicken pop() {
if (count == 0) {
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
}
}
class Product implements Runnable {
private SynContainer container;
public Product(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了第" + i + "只鸡");
}
}
}
class Consumer implements Runnable {
private SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了第" + container.pop().id + "只鸡");
}
}
}
线程池
t = new TestThread();
Thread t1 = new Thread(new TestThread1());
//提交线程执行
ser.submit(t);//Callable 接口
ser.execute(t1);//Runable 接口
//关闭服务
ser.shutdown();
}
}
class TestThread implements Callable<Boolean> {
@Override
public Boolean call(){
System.out.println("线程"+Thread.currentThread().getName()+" ");
return true;
}
}
class TestThread1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"");
}
}
八大排序与查找
二分查找
1.冒泡排序
//冒泡排序
for (int i = 0; i < arr.length - 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;
}
}
}
2.选择排序
//选择
arr = new int[]{1, 2, 67, 5, 74, 6};
for (int i = 0; i < arr.length ; 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.插入排序
//直接插入
arr = new int[]{1, 2, 67, 5, 74, 6};
for (int i = 1; i < arr.length; i++) {
int j=i;
while(j>0&&arr[j]<arr[j-1]){
int t = arr[j];
arr[j] = arr[j-1];
arr[j-1] = t;
j--;
}
}
4.希尔排序
public static int[] ShellSort(int[] arr){
//希尔排序是对插入排序的优化
//装量选取最好的是克努特序列 h=1 h=h*3+1 1 4 13 40 121 364 ...
int jiange =1;
while(jiange<arr.length/3)
jiange = jiange*3+1;
for(int h = jiange;h>0;h=(h-1)/3 ){
for (int i = h; i < arr.length; i++) {
for (int j = i; j >h-1 ; j-=h) {
if(arr[j]<arr[j-h]){
int t = arr[j];
arr[j]=arr[j-h];
arr[j-h]=t;}
}
}
}
return arr;
}
5.快速排序
//快速排序主要是找到index的过程,然后将大于index的饭在右边小的放左边g
//然后使用归并 左右两边
//出口是start<end
//快速排序
private void quit_sort (int[] nums,int s,int e){
if(s>=e) return;
int index = findIndex (nums,s,e);
quit_sort(nums,s,index);
quit_sort(nums,index+1,e);
}
//找index的過程是挖坑法
private int findIndex(int[] nums,int s,int e){
int index = nums[s];
while(s<e){
while(s<e&&index<=nums[e]) e--;
nums[s] = nums[e];
while(s<e&&index>=nums[s]) s++;
nums[e] = nums[s];
}
nums[s] = index;
return s;
}
6.归并排序
private static void Guibin(int []arr,int start, int end) {//拆分
int center = (start+end)/2;
if(start<end){
Guibin(arr,start,center);
Guibin(arr,center+1,end);
hebing(arr,start,center,end);
}
}
private static void hebing(int[] arr, int start, int center, int end) {//合并
int[]tmpArr = new int[end-start+1];
int i = start;
int j = center+1;
int index = 0;
while (i<=center&&j<=end){
if(arr[i]<=arr[j]){
tmpArr[index++]=arr[i++];
}else{
tmpArr[index++]=arr[j++];
}
}
while(i<=center){
tmpArr[index++]=arr[i++];
}
while(j<=end){
tmpArr[index++]=arr[j++];
}
for (int k = 0; k < tmpArr.length; k++) {
arr[k+start]=tmpArr[k];
}
}
7.堆排序
//堆排序
private int[] sheap_sotr(int []nums){
//建堆 大根堆
for (int i = nums.length; i >0 ; i--) { //从尾结点收缩是有说法的 如果从头结点,不好建堆
creat_heap(nums,i);
//和头结点交换
int tmp = nums[0];
nums[0] = nums[i-1];
nums[i-1] = tmp;
}
return nums;
}
private void creat_heap(int[] nums, int end) {
//从下往上建堆 从下往上不能从上往下
for(int i=end/2-1;i>=0;i--){
if(nums[i]<=nums[2*i+1]&&2*i+1<end){ //这个&&是重点
int tmp = nums[i];
nums[i]=nums[2*i+1];
nums[2*i+1] = tmp;
}
if(nums[i]<=nums[2*i+2]&&2*i+2<end){
int tmp = nums[i];
nums[i]=nums[2*i+2];
nums[2*i+2] = tmp;
}
}
}
for (int cc : count){
cc = 0;
}
//原数组没变化
int a = 1;//等价于给数组赋值
int b = a;//b指向数组a 也就相当于b是元素变量
b = 2;//更改元素变量
System.out.println(a);//a的值依然是1,只是元素变量改变了而已
8.基数排序 桶排序
private int[] radix_sort(int []nums,int radix){
int maxLenth = 0;
for(int num: nums){
if(String.valueOf(num).length() > maxLenth)
maxLenth = String.valueOf(num).length();
}
int[] count = new int[radix];
int[] bucket = new int [nums.length];
int radixNum = 1;
for (int i = 0; i < maxLenth; i++) {
//将每个数的第i+1个数放到count中
for (int j = 0; j < nums.length; j++) {
count[nums[j]/radixNum%radix]++;
}
//将其排序
for (int j = 1; j < radix ; j++) {
count[j] = count[j]+count[j-1];//这步真的是精髓
}
//將数字根据count中的值放在桶中
for (int j = nums.length-1; j >=0 ; j--) {//因为是像栈一样的,所以要反过来
bucket[ count[nums[j]/radixNum%radix] - 1 ] = nums[j]; //bucket的下标对应桶的排序
count[nums[j]/radixNum%radix]--; //处理重复 2 -> 1
}
//将通中的数据放在nums 进行下一位数的排序
nums = Arrays.copyOfRange(bucket,0,nums.length);
//count[]归0
for (int j = 0; j < radix; j++) {
count[j] = 0;
}
//位数
radixNum *=radix;
}
return nums;
}
;
}
}
while(i<=center){
tmpArr[index++]=arr[i++];
}
while(j<=end){
tmpArr[index++]=arr[j++];
}
for (int k = 0; k < tmpArr.length; k++) {
arr[k+start]=tmpArr[k];
}
}
7.堆排序
//堆排序
private int[] sheap_sotr(int []nums){
//建堆 大根堆
for (int i = nums.length; i >0 ; i--) { //从尾结点收缩是有说法的 如果从头结点,不好建堆
creat_heap(nums,i);
//和头结点交换
int tmp = nums[0];
nums[0] = nums[i-1];
nums[i-1] = tmp;
}
return nums;
}
private void creat_heap(int[] nums, int end) {
//从下往上建堆 从下往上不能从上往下
for(int i=end/2-1;i>=0;i--){
if(nums[i]<=nums[2*i+1]&&2*i+1<end){ //这个&&是重点
int tmp = nums[i];
nums[i]=nums[2*i+1];
nums[2*i+1] = tmp;
}
if(nums[i]<=nums[2*i+2]&&2*i+2<end){
int tmp = nums[i];
nums[i]=nums[2*i+2];
nums[2*i+2] = tmp;
}
}
}
for (int cc : count){
cc = 0;
}
//原数组没变化
int a = 1;//等价于给数组赋值
int b = a;//b指向数组a 也就相当于b是元素变量
b = 2;//更改元素变量
System.out.println(a);//a的值依然是1,只是元素变量改变了而已
8.基数排序 桶排序
private int[] radix_sort(int []nums,int radix){
int maxLenth = 0;
for(int num: nums){
if(String.valueOf(num).length() > maxLenth)
maxLenth = String.valueOf(num).length();
}
int[] count = new int[radix];
int[] bucket = new int [nums.length];
int radixNum = 1;
for (int i = 0; i < maxLenth; i++) {
//将每个数的第i+1个数放到count中
for (int j = 0; j < nums.length; j++) {
count[nums[j]/radixNum%radix]++;
}
//将其排序
for (int j = 1; j < radix ; j++) {
count[j] = count[j]+count[j-1];//这步真的是精髓
}
//將数字根据count中的值放在桶中
for (int j = nums.length-1; j >=0 ; j--) {//因为是像栈一样的,所以要反过来
bucket[ count[nums[j]/radixNum%radix] - 1 ] = nums[j]; //bucket的下标对应桶的排序
count[nums[j]/radixNum%radix]--; //处理重复 2 -> 1
}
//将通中的数据放在nums 进行下一位数的排序
nums = Arrays.copyOfRange(bucket,0,nums.length);
//count[]归0
for (int j = 0; j < radix; j++) {
count[j] = 0;
}
//位数
radixNum *=radix;
}
return nums;
}