se基础
万物皆对象,比如在开发过程中,前端的一个请求就是一个对象,request对象。Class对象 , 异常在java中也是一个类,发生一个异常系统也就会生成一个异常对象。
面向对象三大特征:封装、继承、多态。
2.继承:就是子类继承父类的所有财产,但是实际应用中子类只能继承一个父类,而实际开发中一个类可能需要使用多个父类中的东西,这就出现了接口。一个类可以实现多个接口。(单继承吗,多继承)
3.多态: 父类引用向子类对象。比如我们经常使用的就是,List list = new ArrayList(); 在实际开发中也可以说是解耦吧,比如经理让你造一辆车,给你个父类,你只需在子类中实现功能。另一个同事也可以去造一个另一辆车,实现其他的功能。
Java有自己的虚拟机,好处就是一次编译处处运行。
JDK 是java开发工具包,里面包括JRE(java运行环境)和JVM
Jvm是建立在os操作系统上,相当于一个专为java开发用的虚拟计算机,里面有相应的堆,栈,方法区等。
JRE是java运行环境,里面有java运行时所需要的核心类库。
(1) Jdk7及以前,接口中只能定义全局常量,和抽象方法
全局常量:public static final (但是书写时,可以省略)
抽象方法:pubic abstract
Jdk8, 除了可以定义全局常量和抽象方法之外,还可以定义静态方法,(default)默认方法。
(2)stream流 :一种流的思想,好比一个流水线,对数组和集合进行高效的操作。
Stream流的三类方法:
1,获取Stream流。
2,中间方法(skip跳过前几个,limit截取前几个, concat(Stream a , Stream b)合并流 filter 过滤)
3,终结方法,流水线上最后一个操作。 foeach(在foreach中可以新建一个集合用来结果封装), 和 count
(3)Lamba表达式:一种简化匿名内部类的开发方式。是一种函数式思想。
面向对象的思想:做一件事,找到一个能做这件事的对象,调用对象的方法。
函数式编程的思想:只要能获得结果,谁去做都行。
(4)Hashset 底层是哈希表 jdk8之前是数组+链表jdk8之后是数组+链表+红黑树
,1.8当链表长度为8时,再次添加自动将链表转换为红黑树。
抽象类:指的是用abstract修饰的类或者类中有抽象方法。
接口:只有方法体没有具体的实现。用interface修饰
接口是一种规范,是抽象类的的变体。一个方法可以实现多个接口,但是只能继承一个抽象类。
在实际项目开发中,架构师一般会定义好接口,我们只需要在具体的业务中对接口进行实现。在多变的业务中,我们只需要改变相应的实现类。
如果在业务中,多个实现类有着相同可以复用的代码,这时候我们可以在实现类和接口之间,添加一个抽象类,把公共的代码抽取道抽象类中。
集合,数组
进程:一个在内存中运行的应用程序。一个进程可以有多个线程。比如360,可以一边杀毒,一边清理内存,他就是一个单进程,多线程的程序。
从操作系统角度上说:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
从JVM的角度上说一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 元空间,方法区只是一个抽象的概念,元空间是具体的实现 )资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承
finally是异常处理语句结构的一部分,表示总是执行。
finalize,Object类的一个方法,在垃圾回收器执行的时候会被调用。当该方法被系统调用时则代表该对象即将“死亡”,但需要注意的是,我们主动行为上去调用该方法不会导致对象“死亡”,这是一个被动的方法,不需要我们调用。
定义的位置不一样【重点】
局部变量:在方法的内部
成员变量:在方法的外部,直接写在类当中
作用范围不一样【重点】
局部变量:只有在方法当中才可以使用,出了方法就不能再用了
成员变量:整个类全都可以通用
默认值不一样【重点】
局部变量:没有默认值,如果要想使用,必须手动进行赋值
成员变量:如果没有赋值,会有默认值,规则和数组一样
内存的位置不一样(了解)
局部变量:位于栈(Stack)内存
成员变量:位于堆(Teap)内存
生命周期不一样(了解)
局部变量:随着方法进栈而诞生,随着方法出栈而消失
变量:随着类的创建而诞生,随着对象被垃圾回收而消失(静态方法使用局部变量,局部变量必须为final修饰)
**按值传递:**指在方法调用时,传递的参数是按照值得拷贝进行传递的。
**引用传递:**指的是在方法调用时,传递的参数是按引用进行传递,也就是变量所对应的内存空间的地址。
高斯林认为在JAVA中传递的方式都是为值传递,没有引用传递。(将引用地址看做值)
一般情况下,在数据做为参数传递的时候,基本数据类型是值传递,引用数据类型是引用传递(地址传递)。 String(字符串常量,常量值创建之后就不能改变)
==比较的是两个值的地址是否相同, equals是比较字符串的值是否相同
在Java中任何一个对象都具备equals(Object obj)和hashCode()这两个方法,因为他们是在Object类中定义的
Hashcode方法,是将对象的地址值进行hash运算,得到的一组hash值。
可以看到,在Object类型的equals方法是直接通过来比较的,和是没有任何区别的。
那么为什么又要说equlas和==的区别呢?是因为equals方法是可以由我们自己重写的。
在hashMap中,Hashcode计算出来的哈希值相同,两个字符串的值不一定相同,所以在hashmap中存的类型,都得重写,hashcode方法和equals方法,是先进行hash值的比较,再进行equals的比较。
13,重载和重写区别?
重载:就是两个方法,方法名相同,参数列表不同,与返回值无关,只和方法名,参数列表,参数的类型有关.
比如:一般会出现类构造方法的重载,有参,和无参,还有在实际业务接口中,你想用id查询数据,或者用名称查询数据,都提供了响应的重载方法。
(1):方法名必须相同
(2):方法的参数列表一定不一样。
(3):访问修饰符和返回值类型可以相同也可以不同。
重写:方法名相同,参数相同,但是具体的实现不同。一般都是子类对父类方法的增强。或者接口,的实现类进行重写接口中的方法。
整型byte 、short 、int 、long 浮点型float 、 double 字符型char 布尔型boolean
包装类就是对基本数据类型的增强,解决基本数据类型无法参与反射,泛型等问题。
java是面向对象进行编程的,而基本数据类型不是对象。
基本类不能为空,有默认值。
包装类可以为空。可以在泛型中使用。
自动装箱拆箱.
Static修饰会随着类的加载,而进入方法区。
这是父类的静态代码快
这是子类的静态代码快
这是父类的构造代码快
这是父类的构造方法
这是子类的构造代码快
这是子类的构造方法
public parent() { System.out.println("父类构造函数"); } static{ System.out.println("父类静态块"); } { System.out.println("父类构造代码f块"); }
不能,因为看源码是被final修饰的,被final修饰的类不能被继承
IntegerCache.cache数组中至少缓存[-128,127]范围的Integer实例。 在这个区间类直接从缓冲区拿来用。
当数值不处于 【-128,127】区间时,将会直接 new Integer();进行新创建一个对象。
装箱:Integer a = 10;
拆箱:将包装类赋值给 基本数据类型
String类:字符串是常量,它的值在创建之后就不能被改变了。每对String进行操作,都会生成新的String对象,不仅效率低下,而且浪费内存。
StringBuffer、StringBuild的出现是为了提高字符串的操作效率。
StringBuffer:(线程安全) 拼接方法append(),看源码相比于StringBuild每个方法都加了synchronized 关键字 ,成为了同步方法。
StringBuild:(线程不安全)
String底层也是用的StringBuilder
StringBuilder在拼接的时候,只需要在堆内存中new一个对象就行了,然后调用append进行拼接。
集合可以分为两类:
(1)单列集合,Vector jdk1.0 最早的单列集合。底层也是一个数组和arrayList一样。Vector是同步的。同步就是单线程。(单线程就是慢)1.2后用ArrayList集合给取代了。
(2)双列集合
集合知识点学习:
Set(单列集合) 实现类: treeset 和 hashset 特点:不可以重复,存取顺序不一致。没有索引,所以不能通过普通for进行循环遍历。
Treeset 可以将元素按照规则进行排序
Hashset 底层基于hash表 hash值,Object中的方法,hashcode根据对象的地址计算hash值。 重写 hashcode方法,按照对象的属性值来计算hash值,跟对象的地址值就没有关系了。
List: 实现类: arraylist: 底层是数组结构,查询快,增删慢 , linkedlist:底层是链表结构,查询慢,增删快。
(1)自然排序 创建一个类 实现 Comparable 接口
使用:
使用空参构造创建Treeset 集合
自定义Student类实现Comparable 接口,
重写里面的compareTo方法
Int 为负数时,表示当前元素比较小,存左边, 为0 ,重复,不存
(2)比较器排序 Comparator
使用:让集合的构造方法接收一个Comparator的实现类对象,重写compare(T01 ,T02)方法,可以定义主要排序条件和次要排序条件
**比较:**comparable是需要比较的对象来实现接口(内部比较器)。这样对对象的耦合度高(需要改变对象的内部结构,破坏性大,想更改比较方式就得在类中进行修改)。Comparator只需在treeset的构造中传一个比较器就行了,解耦。还可以实现多个比较方式
Collection是集合类的上级接口,子接口有 Set、List、LinkedList、ArrayList、Vector、Stack、Set;
Collections是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
1,安全问题:
HashMap是线程不安全的,在多线程并发的环境下。会出现问题(在多线程环境下,给hashmap中存值时,会出现null值)。
Hashtable是线程安全的,它的每个方法上都有synchronized 关键字,直接对hash表进行加锁。
但是 Hashtable效率低。所以在线程安全的问题下用 ConcurrentHashMap, ConcurrentHashMap不是对整个数组进行加锁,而是使用*锁分段技术。初始化长度为16的数组,每一个元素下面还有HashEntey的小数组,加锁是加在这些H
ashEntey的小数组上。每次最多允许16个线程同时访问( 1.7)
(1.8,CAS自旋(乐观锁)) 底层:哈希表(数组,链表,红黑树) ConcurrentHashMap1.7和1.8不同
机制:CAS算法,+ synchronized同步代码块。当线程操作数组中的一个元素时,以链表或红黑树的头节点作为锁对象。
对null的支持不同 :Hashtable:key和value都不能为null。 HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key 值对应的value为null。
这是数据结构中的散列表(hash表)处理散列冲突的问题:
**1,开放定址法:**一旦发生冲突,就去寻找下一个空的散列地址。只要散列表足够大。空的散列地址总能找到,并将记录存入。(mod(取模)hash数组长度,有散列函数)
根据散列函数算出地址,如果有冲突在原来的散列函数上加一个偏移量。
1,按照偏移量的不同分为线性探测,(设置增量序列 1,2,3.。。。)平方探测,双散列。
2,拉链法: 每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储.
Resize扩容方法
List有索引 linkedlist , ArrayList 可重复
Set无索引 hashset, treeset 不可重复
Map 使用键值对存储,key不可重复,存key的其实就是一个set集合
List—是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承Collection。 List有两个重要的实现类:ArrayList和LinkedList。
ArrayList: 可以看作是能够自动增长容量的数组 ArrayList的toArray方法返回一个数组 ArrayList的asList方法返回一个列表 ArrayList底层的实现是Array, 数组扩容实现。
LinkList:是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于 ArrayList.当然,这些对比都是指数据量很大或者操作很频繁。
Vector jdk1.0 最早的单列集合。底层也是一个数组和arrayList一样。Vector是同步的。同步就是单线程。(单线程就是慢)1.2后用ArrayList集合给取代了。
Arrylist底层是数组实现的,数组在内存中存储需要完整的内存空间
返回包含此集合中所有元素的数组;
1.8中创建hashmap时,不会创建数组,当第一次插入数据时会创建一个16的Node[]数组;
当存入数据时,会根据数据的key的hashcode使用散列函数进行计算获得该数据在数组中的索引值:
当该索引处没有数据时,直接插入该索引处即可
当该索引处有数据时,判断两个数据的key的hashcode是否相同
当hashcode不相同时,则认为两个对象不同,将数据存入该索引的链表
当hashcode相同时,判断equals方法
当equals方法为false,则不同,将数据存入链表中
当equals方法为true,则相同,使用新的value替换旧的value
此时插入操作结束。
当插入时该链表上的节点个数大于等于8时,会判断数组长度是否大于等于64:
当数组长度小于64,则应当扩容
当数组长度大于等于64,应当将该链表修改为红黑树
扩容:1.当集合元素个数/数组长度>=加载因子0.75时,扩容
2.当某个链表长度大于等于8,且数组长度小于64,扩容
Mt.run() : 在main线程中执行,属于单线程 。 Mt.start();开辟一个新的栈空间,执行run方法。
MyThread.start JVM会请求OS(操作系统)开辟一条线程。然后在jvm中创建这个线程的栈内存。然后两个线程,一个main线程,一个新线程一起抢夺cpu 的执行权。 方法在栈中执行,先开始main方法会压栈执行。一行一行执行里面的代码。
**多线程的好处:**多个线程之间互不影响(不同的栈空间) 一个报错,一个还可以运行。
(1)继承Thread类 重写run方法 然后用的时候new Thread ,thread.start
(2)实现Runnable接口 重写 run方法 避免了单继承的局限性,并且把设置任务和开启线程进行了分离(解耦)
Thread th = new Thread; thread.start(new Runnable);
(3)实现Callable 接口重写 call方法 可以加返回值 用futureTask接收
public class Test { public static void main(String[] args) throws Exception { //创建任务 MyCallable mc=new MyCallable(); /**FutureTask同时实现了Runnable,Future接口。 - 它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值 */ //交付任务管理 FutureTask<String> task=new FutureTask<>(mc);//可以看成FutureTask实现了runnable接口 Thread t=new Thread(task); t.start(); System.out.println("获取结果:"+task.get()); System.out.println("任务是否完成:"+task.isDone()); } } //实现Callable接口,重写call方法 class MyCallable implements Callable { @Override public Object call() throws Exception { String [] str= {"apple","pear","banana","orange","grape"}; int i=(int)(Math.random()*5); return str[i]; } }
(4)线程池
new ThreadPoolExecutor(7个参数)创建线程:核心线程数、最大线程数、空闲线程存活时间、时间单位、阻塞队列、创建线程的工厂、超出任务拒绝策略;
核心线程数、最大线程数、空闲线程存活时间、时间单位、阻塞队列、创建线程的工厂、超出任务拒绝策略
1,corePoolSize 线程池核心线程大小:线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁
2,maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
3,keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁
4,unit 空闲线程存活时间单位
5,workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
6,threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
7,handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,
超出任务拒绝策略:
ThreadPoolExecutor.Abortpolicy 丢弃任务,并抛出异常,默认策略
ThreadPoolExecutor.DiscardPolicy 丢弃任务,不抛出异常
ThreadPoolExecutor.DisOldestPolicy 丢弃等待时间最久的任务,把当前任务加入等待队列
ThreadPoolExecutor.CallerRunsPolicy 调用任务的run()方法绕过线程池执行。
1,FixedThreadPool :使用固定线程数的线程池
Executors.newFixedThreadPool(threadsNum)
。2,CachedThreadPool
Executors.newCachedThreadPool()
。3,SingleThreadExecutor
Executors.newSingleThreadScheduledExecutor()
。4,ScheduledThreadPool
线程的的状态在JDK1.5之后被定义在Thread的源码之中,有6种状态
(1)NEW 新建状态,线程被new出来,MyThread t = new MyThread();
线程刚被创建出来,但并未启动。
(2)RUNNEBLE 就绪状态, 这是我们对线程调用了start方法,此时才真正的在JVM中创建了一个线程,此时线程可以开始运行,或者在排队等待操作系统给它分配cpu资源。
(3)BLOCKED 阻塞等待锁的线程状态,当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。synchronized
(4)WAITING wait(); notify()
(5)TIMED_WAITING sleep(1000); 计时等待
(6)TERMINATED(terminated 结束) 终止
线程安全问题的产生,多个线程同时访问同一个资源。
解决线程安全问题的方法有3种:
(1)同步代码块: 把同步代码块锁住,只允许有一个线程在代码块中进行执行
没有获取锁的线程进入阻塞状态,一直等待使用锁的线程归还锁。
这种方式有缺点,程序需要去判断锁,获取锁,释放锁,程序的效率会降低
(2)同步方法 也可以是静态同步方法。(static synchronized )
定义一个方法上面加上synchronized 关键字。
同步方法的锁是new RunnableImpl 也就是this
静态同步方法的锁是本类的class文件对象
(3)lock锁机制。使用它的子类 reentrantLock(ri en tree t lock)
一般把unlock放到finally中
Wait和sleep方法都能是线程处于等待状态,
区别:wait会释放锁,sleep不会,sleep属于Thread类中的方法,使线程休眠一段时间,自动唤醒进入到可运行状态。Wait属于object中的方法,一旦调用了wait方法,必须采用notify或notifyAll方法唤醒该进程。
Notify: 唤醒wait set 中等待时间最长的线程,进入调度队列(ready queue(发音Q))中
因为它当初中断的地方在同步代码块中,而此刻它又不持有锁,所以它要去尝试获取锁(可能面临其他线程的竞争),成功获取锁后,才能执行wait之后的代码。如果不成功进入entry set, 线程从waiting状态进入blocked状态。
NotifyAll: 唤醒所有 在wait set中的线程,
死锁:当锁进行嵌套时就会出现死锁。两个或多个线程互相持有对方的资源导致上方都处于等待状态。无法执行。
防止死锁,就是不要写锁的嵌套
悲观锁:synchronized,从最坏的角度来看,认为每次获取共享数据的时候,都会被别的线程修改。所以给次在操作共享共享数据之前都要加锁。
乐观锁:cas算法,从乐观角度出发。假如每次获取共享数据别人都不会修改。所以不会上锁。只不过在修改共享数据的时候,会检查一下,有没有别人修改。如果修改,获取最新值。
多线程之间的通信:
cpu随机执行多线程,当我们需要多线程来共同完成一件任务,需要他们有规律的执行----等待唤醒机制
Wait : 线程进入 wait set 中 , 不会参与调度。等待其他线程执行notify操作。
Notify: 唤醒wait set 中等待时间最长的线程,进入调度队列(ready queue(发音Q))中
因为它当初中断的地方在同步代码块中,而此刻它又不持有锁,所以它要去尝试获取锁(可能面临其他线程的竞争),成功获取锁后,才能执行wait之后的代码。如果不成功进入entry set, 线程从waiting状态进入blocked状态。
NotifyAll: 唤醒所有 在wait set中的线程,
**注意:**wait方法和notify方法必须在同步代码块或者同步方法中执行。必须要用锁对象调用这两个方法。
**死锁:**锁进行了嵌套。
**乐观锁:**cas读过来,存在自己线程的线程副本中。
**原子类:**AtomicInterger(); (饿涛妹可)
线程工具类:
CountDownLatch A线程等待B,C,D线程执行完后再进行执行
信号量Semaphore(森魔for): 有多个线程访问,一个接口,只允许1分钟内访问100次。
有参方法tryAcquire(int long timeout,TimeUnit unit)的作用是在指定的时间内尝试地获得1个许可,如果获取不到则返回false。
Semaphore semaphore = new Semaphore(1); if (semaphore.tryAcquire(2, TimeUnit.SECONDS)) { System.*out*.println(Thread.*currentThread*().getName() + "执行时间:" + System.*currentTimeMillis*()); semaphore.release(2); }
TCP:面向连接的,速度慢,传输的效率大小无限制;先建立连接,确定连接无误后,才进行数据的传输。数据传输完成断开连接,通常用于上传下载文件。
UDP: 面向用户的,速度快,传输的数据最大64K; 发送端和接收端无需建立连接。通常用于视频通话,直播。可以允许丢失包。一般有三个模式:单播,组播,广播。
TCP三次握手,四次握手
三次握手我的理解就是;(1)客户端给服务端,说我是客户端我要请求,(2)
服务端然后给客户端说可以对我进行请求,(3)客户端这时候就可以给服务端发起请求。
四次握手:
A:B 啊,我不想玩了
B:哦,你不想玩了啊,我知道了
这个时候,只是 A 不想玩了,即不再发送数据,但是 B 可能还有未发送完的数据,所以需要等待 B 也主动关闭。
B:A 啊,好吧,我也不玩了,拜拜
A:好的,拜拜
http的缺点:
Http不安全,通信采用明文,内容可能被监听
https,采用SSL协议进行加密
得先有个域名,然后在阿里云上申请SSL证书,然后在Tomcat中进行配置
75, 十进制小数在转换成2进制会有精度丢失。0.9在二进制存储时是11001001…底层原理就是,十进制整数在转化为二进制时不会出现精度问题,把十进制小数扩大N倍,让他在整数的维度上进行计算,保留相应的精度信息。
(1) 截取,身份证的几位,和几位,
//如果身份证格式是没有问题的,那个打印年月日信息 year = IDNumber.substring(6,10); month = IDNumber.substring(10,12); day = IDNumber.substring(12,14);
(2) 获取字符串长度
int len = str.length();
(3) 去除字符串首尾的空格
String str = " ab cde "; String str1 = str.trim();//str1为"ab cde" (tri m)
(4) 字符串分割
String[] split():根据匹配给定的正则表达式来拆分字符串,将分割后的结果存入字符数组中。
String[] split(String regex):regex为正则表达式分隔符, . 、 $、 | 和 * 等转义字符,必须得加 \;多个分隔符,可以用 | 作为连字符。
String[] split(String regex, int limit):limit为分割份数
String str = "Hello World A.B.C" String[] res = str.split(" ");//res = {"Hello","World","A.B.C"} String[] res = str.split(" ",2);//res = {"Hello","World A.B.C"} String[] res = str.split("\\.");//res = {"Hello World A","B","C"} String str = "A=1 and B=2 or C=3" String[] res = str.split("and|or");//res = {"A=1 "," B=2 "," C=3"}
(5)字符串与byte数组的转换
byte[] Str2 = Str1.getBytes();
(6) 替换
String str = "abc"; String s = "1"; str.replace(1,1,s);*//此时str为"a1c"*
(7) 工具类
StringUtils.isBlank("") = true 判断某字符串是否为空或长度为0或由空白符(whitespace) 构成
StringUtils.isEmpty(null) = true 判断某字符串是否为空,为空的标准是 str==null 或 str.length()==0
学习这个之前先了解这几个名词,阻塞和非阻塞,同步和异步。
1.1阻塞和非阻塞:阻塞和非阻塞,是进程在访问数据的时候,数据缓冲区内是否准备就绪,的一种处理方式。 当数据没有准备的时候 阻塞:往往需要等待缓冲区中的数据准备好过后,进程才可以处理其他的事情。否则会一直等待在哪里。
非阻塞:当我们的进程访问我们的数据缓冲区的时候,数据没有准备好,直接返回,
1.2同步和异步
晚上竟然没有网, 自己说了:同步,在线程进行io时不能做其他的事情,阻塞就等待, 而异步,阻塞,就可以干其他的事情。
BIO就是阻塞线程,1.5之前使用
NIO 是有个Select线程进行轮询,请求资源如果没有就可以去干其他事,等待交给select线程 目前大多框架使用 Netty 框架使用,dubbo就是基于Netty