赞
踩
重载:
意义:重载就是为了方便外界对方法进行调用,什么样的参数程序都可以找到对应的方法来执行,体现的是程序的灵活性
重载就是在同一个类中出现方法名相同,但是参数列表不同方法的现象
并且方法之间能否构成重载,取决于方法的参数个数与类型,与方法的参数名无关
<拓展内容>
方法的传值:基本类型传递的是实际值,引用类型传递的是地址
并且方法的参数属于形参,只是格式上需要定义,但是调用方法时起不到限制的作用
形参:定义方法的时候的参数列表
实参:使用方法的时候传入的数据:
重写:
表述:子类继承父类以后,如果子类对父类的功能不满意,可以重写父类的方法
意义:是在不修改源码的前提下,进行功能的修改和拓展
遵循规则:两同两小一大
一大:子类方法的修饰符范围 >= 父类方法的修饰符范围–指的是访问控制符
两同:方法名相同,参数列表相同
两小: 子类方法的返回值类型 <= 父类方法的返回值类型【这个大小是继承关系,不是值的大小】
子类方法抛出的异常类型 <= 父类方法抛出的异常类型
注意:如果父类方法的返回值类型是void,子类保持一致即可
注意:子类不可以重写父类的私有方法,还是因为不可见
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
补充:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
优点:
1)降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2)提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
3)提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机,参考运行内存8G)。
线程生命周期,主要有五种状态:
1)新建状态(New) 当线程对象创建后就进入了新建状态,如:Thread t = new MyThread();
2)就绪状态(Runnable)当调用线程对象的start()方法,线程即为进入就绪状态.
处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行。
3)运行状态(Running)当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态。
就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态。
4)阻塞状态(Blocked)处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行。
根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期
String
特点:创建之后长度内容是不可变的,每次拼接字符串,都会产生新的对象
<拓展理解>【你这样想,因为它的长度是不可变的,所以String 是不可变的对象, 每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象(因为长度改变了变成一个新的string),然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的】
如果是直接“ ” 或者字符串常量拼接产生的,保存在字符串常量池中
如果是直接通过new方式创建的,保存在堆中
创建方式:
String() String(String s) String(char[] c) String(byte[] b) String s = “abc”;
string的优点:String类提供了丰富的关于操作字符串的方法,比如:拼接、获取对应下标处的字符、截取子串等
String转换成StringBuilder>>>
第一步:String s = “abc”;
第二步:StringBuilder sb = new StringBuilder(s):
StringBuilder转换成String>>>
StringBuilder sb = new StringBuilder();
sb.append(“abc”);
String s = sb.toString();
StringBuilder
特点:StringBuilder是一个长度可变的字符串序列,在创建的时候,会有一个长度为16的默认空间。当拼接字符串的时候,实在原对象的基础之上进行拼接,如果长度不够就扩容,所以StringBuilder在创建之后,对应的操作一直是用一个对象
创建方式:
StringBuilder sb = new StringBuilder();//创建一个长度为16的StringBuilder对象
StringBuilder sb = new StringBuilder(“abc”);//以指定字符串内容为“abc”的方式创建一个StringBuilder对象
StringBuilder的优缺点:
优点:在拼接的时候,不会产生新的对象,避免了因为拼接频繁生成对象的问题,提高了程序的效率
缺点:对于字符串的操作,不太方便,所以在使用的时候,如果拼接操作很多的话。
先将String转为StringBuilder进行拼接,拼接完成之后再转回String
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法(比如String toString():将对象变成字符串,Class getClass():获取任意对象运行时的所属字节码文件对象),集合中元素自身方法无法使用。
泛型的上下限是泛型的高级使用指的是受限的泛型
泛型上限:
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型下限:
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
通俗的从数据库角度说,事务本身就是一组SQL指令,它要么全部执行成功,如果因为某个原因使得其中一条指令执行有错误,则撤销先前执行过的所有指令。也就是说要么全部执行成功,要么撤销不执行。
Java事务是什么?如果在一个Java的应用系统里,你如果要操作数据库,那就需要通过JDBC来实现的。比如增删改都是通过相应方法间接来实现,事务的控制也相应转移到了那个Java程序代码中。数据库操作的事务习惯上就称为Java事务!
数据库事务的隔离级别有4种
分别为Read uncommitted(读未提交)
Read committed(读已提交)
Repeatable read (可重复读)
Serializable(串行化顺序执行)
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
静态常量池
即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:字面量和符号引用量,字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,
运行时常量池
jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。
String.intern() 方法简单来说可以使得所有含相同内容的字符串都共享同一个内存对象,可以节省内存空间。
首先先和面向过程进行一个对比,从两种不同的处理问题角度看下两种思想
面向过程是更注重一个步骤和顺序,面向对象是是注重有哪些参与者,以及各自需要做什么
面向过程举例—洗衣服
步骤: 打开洗衣机——放衣服——放洗衣液——点击开始按钮——烘干
面向对象是考虑—— 人:负责打开洗衣机 洗衣机:清洗 烘干
总结就是面向过程比较高效,但面向对象易于复用,扩展以及维护
展开讲面向对象有三大特性:
1.封装——意义在于明确标识出允许外部使用的的成员函数和数据,内部细节对外部调用透明,外部调用无需更改或者内部实现。
2.继承——子类extends父类,子类共性的方法或者属性直接使用父类的,不用自己定义,只需扩展自己个性化的。
3.多态——它是基于条件所属类不同,外部对同一个方法的调用,实际执行的逻辑不同
前提条件(继承/重写/父类引用指向子类对象) 但是有个弊端无法调用子类特有的功能
父类类型 变量名=new 子类对象
变量名.子类方法名
先说下各自的作用:
JDK—java的开发工具
JRE—运行时环境
JVM—虚拟机 将java文件转换成可识别的.class文件
其中在JDK中的JRE文件中有bin(jvm)和lib(jdk的类库)两个子文件夹,二者合就是jre
MyBatis 封装了底层 JDBC API 的调用细节,简化了JDBC的操作,并且会自动生成接口的具体实现类,方便调用。MyBatis会把sql语句从Java源程序中独立出来,便于程序的维护,Mybatis实现了DAO接口与xml映射文件的绑定。
#{}是预编译处理,$ {}是字符串替换。
MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
使用#{}可以有效的防止 SQL 注入,提高系统安全性。
String是final修饰的 不可变 每次操作都会产生薪的string对象(浪费内存)
stringBuffer和stringBuilder都是在原对象上操作(后面都是拼接)
stringBuffer是线程安全的,stringBuilder是线程不安全的
stringBuffer方法都是synchronized修饰的
性能:stringBuilder>stringBuffer>string
场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
equals是提供程序员比较两个对象?对比规则是什么?
如果equals不重写,默认就是==,对比的就是引用地址
第一步先一个配置文件,配置一个包扫描路径
第二步根据路径去获取该路径下的所有.class文件
第三步反射确定要交给IOC管理的类
第四步对需要注入的类进行依赖注入
定义一些注解,表示访问控制层 业务服务层 数据持久层 依赖注入注解 获取配置文件注解
从配置文件中获取需要扫描的包路径,获取当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个set集合中进行存储
遍历这个set集合,获取在类上有指定注解得类,并将其交给IOC容器,定义一个安全的Map用来存储这些对象
遍历这个IOC容器,获取到每一个类的实例,判断里面是否有依赖其它的类的实例,然后进行递归注入。
jdk自带的三个类加载器:
bootstrap/classLoader/Extclassloader /Appclassloader
1.BootStrapClassLoader是exClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和class文件
2.ExClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和class类,3.AppClassLoader是自定义加载器的父类,负责加载classpath下的文件,系统类加载量,线程上下文加载器。
java中的所有异常都来自顶级父类Throwable
Throwable下有两个子类Exception和Error
Error是程序无法处理的错误,一旦出现这个错误,程序将被迫停止运行。
Exception不会导致程序停止,又分为两个部分RuntimeException运行时异常和CheckException检查异常
RuntimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckException常常发生在程序编译过程中,会导致程序编译不通过。
**ArrayList:**基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固定,超出长度的数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(就是往后复制一份,插入薪元素),使用尾插法并指定初始容量可以极大提升性能,甚至超过LinkedList(需要创建大量的node对象)
详细讲解
**LikedList:**基于链表,可以存储在分数的内存中,适合做数据插入及删除操作,不适合查询,需要逐一遍历
遍历LikedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素都需要对list 重新进行遍历,性能消耗极大。
1.线程通常有五种状态,创建,就绪,运行,阻塞和死亡状态
2.阻塞的情况又分为三种:等待阻塞 同步阻塞 其它阻塞
1)新建状态(New):创建一个线程对象
2)就绪状态(Runnable):线程对象创建后,其它线程调用了该对象的start方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3)运行状态(Running):就绪状态的线程获取了CPU,执行程序代码
4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态
5)死亡状态(Dead):线程执行完了或者异常退出run方法,该线程结束生命周期
我们说的线程安全应该是内存安全,堆是共享内容,可以被所有线程访问。
当多个线程访问一个对象时,如果不用进行额外的同步控制活其它的协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的。
**堆是进程和线程共有的空间,分全局堆和局部堆。**全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是用完了要还给操作系统,要不然内存泄漏。
栈是每个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始时候初始化,每个线程的栈互相独立,因此,栈是线程安全的。操作系统在切换线程的时候会自动切换栈。栈空间不需要在高级语言里面显式的分配和释放。
轻量级的开源框架,就是一个容器框架,用来装javabean(java对象),中间层框架(万能胶)可以起一个连接作用,比如说把struts和hibernate粘合在一起运用,可以让我们企业开发更快。
sping是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
—从大小与开销两方面而言spring都是轻量级
—通过控制反转的技术达到松耦合的目的
—提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
—将简单的组件配置,组合成为复杂的应用,这意义上是一个框架
一、十大特性
1.Lambda表达式
2.Stream函数式操作流元素集合
3.接口新增:默认方法与静态方法
4.方法引用,与Lambda表达式联合使用
5.引入重复注解
6.类型注解
7.最新的Date/Time API
8.新增base64加解密API
9.数组并行(parallel)操作
10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
200 – 请求成功
301 – 资源(网页等)被永久转移到其它URL
404 – 请求的资源(网页等)不存在
500 – 内部服务器错误
spring容器其实就是指的是IOC容器(bean)
容器概念
ioc容器:实际上就是个map (key, value), 里面存的是各种对象 (在xml里配置的bean节点@repository. @service、@controller. @component) ,在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创建对象放到map中。
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired、resource等注解,xml里bean节 点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这 些注解,根据类型或id注入,id就是对象名)。
控制反转
控制反转:
没有引入I0C容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候, 自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
依赖注入
引入I0C容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,I0C容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转"这个名称的由来。
全部对象的控制权全部上缴给"第三方"IOC容器,所以I0C容器成了整个系统的关键核心,它起到了类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用, 如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成粘合剂”的由来。
Spring是一个IOC容器,用来管理Bean(bean是对象),使用依赖注入实现控制反转,可以很方便整合各种框架,提供AOP机制,来弥补OOP的代码重复问题,更方便将不同类不同方法中的共同处理抽取成切面、自动注入给方法执行,比如日志,异常等。
SpringMVC是spring对web框架的一个解决方案,提供了一个总的前端控制器servlet,用来接受请求,然后定义了一套路由策略(url的handle的映射)及适配执行handle,将handle结果使用视图解析技术生成视图展现给前端。
SpingBoot是Spring提供的一个快速开发工具包,让程序员更方便,更快速开发spring+springMVC应用,简化了配置(约定了默认配置),整合了一系列的解决方案(starter机制)、redis、mongodb、es可以开箱即用。
先说JDK动态代理原理:
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGLiB动态代理
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
何时使用JDK还是CGLIB
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
spring如何选择用JDK还是CGLIB
1)当Bean实现接口时,Spring就会用JDK的动态代理。
2)当Bean没有实现接口时,Spring使用CGlib是实现。
3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。
我觉得这两者是思考角度的差异,面向过程更多是以“执行者”的角度来思考问题,而面向对象更多是以“组织者”的角度来思考问题,举个例子,比如我要产生一个0-10之间的随机数,如果以“面向过程”的思维,那我更多是关注如何去设计一个算法,然后保证比较均衡产生0-10的随机数,而面向对象的思维会更多关注,我找谁来帮我们做这件事,比如Random类,调用其中提供的方法即可。
所以,面向对象的思维更多的是考虑如何去选择合适的工具,然后组织到一起干一件事。
好比一个导演,要拍一场电影,那么首先要有男猪脚和女猪脚,然后还有其他等等,最后把这些资源组织起来,拍成一场电影。
再说回我们的程序世界,这个组织者的思维无处不在,比如,我们要开发项目,以三层架构的模式来开发,那么这个时候,我们不需要重复造轮子,只需要选择市面上主流的框架即可,比如SpringMVC,Spring,MyBatis,这些都是各层的主流框架。
同第一题
先说二者区别:
抽象类概念:在java世界里所有的对象都是通过类来描绘的,反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这个类就是抽象类。
接口概念 在JAVA编程语言中是接口一个特殊的抽象类型,用interface来声明。一个类通过继承接口的方式,从而来继承接口里面的抽象方法。
总结区别:
1.接口它是可以多继承,而抽象类它只可以单继承!
2.抽象类有构造方法,接口没有构造方法
3.抽象类可以包含普通方法,接口中只能是public abstract修饰抽象方法(Java8之后可以)
4.抽象类可以定义各种类型的成员变量,接口中只能是public static final修饰的静态常量
再来说二者的使用场景:
抽象类的使用场景——当想要实现一个具体的方法的时候,可以用抽象类来继承。
接口的使用场景——当约束多个实现类具有统一的行为共性时候,利用接口的多实现多继承,但是呢接口它不在乎每个实现类如何具体实现。
实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系。
有Collection 和 Map两个容器
具体剖析理解:末尾解答问题
先来比较下StringBuffer和StringBuilder是两个极为相似的两个类,它们都是可变的字符串。可以通过查看源码可知,它们的继承的类以及实现的接口都是一样的。这两个类在实现上不同的地方基本有两点:
StringBuffer在很多方法上加上了关键字synchronized,而StringBuilder没有。
1.StringBuffer在很多方法上加上了关键字synchronized,而StringBuilder没有。
2.StringBuffer新增了一个变量toStringCache,变量toStringCache是toString返回的最后一个值的缓存,每当修改StringBuffer时清除。
除这两点之外,StringBuffer和StringBuilder基本上是一致的,它们的大部分的实现都在于AbstractStringBuilder这个类上。
public final class String implements java.io.Serializable, Comparable, CharSequence {
/** 该值用于字符存储 */
private final char value[];
源码中,使用final关键字修饰了String类和value变量,所以,String类是不可继承的。String所表示的字符串,底层是使用一个字符数组来保存的,也就是value这个变量,此时这个变量也使用final修饰,表示String的表示的字符串是不可变的。所以,每次对 String 变量进行修改的时候其实都等同于生成了一个新的 String 对象。
String 对象的字符串拼接其实是被 JVM 解释成了 StringBuilder对象的拼接,以下面代码为例,下图即是被编译后的字节码,可以看出来String变量的相加其实被翻译成StringBuilder的append方法进行操作。
String x = "abc";
String y = "abc";
String z = x + y + y + y;
String变量不可修改,StringBuffer和StringBuilder可以修改
StringBuffer是线程安全的,StringBuilder不是线程安全的。
StringBuffer使用了缓存区,StringBuilder没有使用缓存区,所以没有修改数据的情况下,多次调用StringBuffer的toString方法获取的字符串是共享底层的字符数组的。而StringBuilder不是共享底层数组的,每次都生成了新的字符数组。
因为方法被上锁,所以StringBuffer的性能一般会比StringBuilder差,单线程中建议使用StringBuilder。
String对象的相加底层调用的是StringBuilder对象,分别调用了append方法和toString方法,所以在大量字符串相加时,使用String对象相加效率低于使用StringBuffer和StringBuilder,因为还有有StringBuilder对象的创建过程和toString方法中字符数组的拷贝过程。
首先HashMap和Hashtable二者都实现了Map接口,但是在决定在Java中是否使用HashMap或Hashtable之前,它们之间有一些重要的区别,就是线程安全、同步和速度。以下是这些不同之处:
1.HashMap与Hashtable的区别是HashMap是非同步的,而Hashtable是同步的,这意味着哈希表线程安全,可以在多个线程之间共享,但是HashMap如果没有适当的同步,就不能在多个线程之间共享。
2.Hashtable和HashMap之间一个更显著的区别是,由于线程安全性和同步性,如果在单线程环境中使用,Hashtable比HashMap慢得多。因此,如果您不需要同步,并且HashMap仅由一个线程使用,那么它的性能将优于Java中的Hashtable。
ArrayList的底层是动态数组,默认初始化大小为10,扩容大小为1.5倍,
arraylist这个集合查询快由于: Array 是基于索引 (index) 的数据结构,它使用索引在数组中搜索和读取数据是很快的。
Array 获取数据的时间复杂度是 O, 可是要删除数据倒是开销很大的,由于这须要重排数组中的全部数据,因此增删除就使用得没那么多,查询快,增删慢。
LinkedList的底层是双向链表,LinkedList将元素添加到链表的末尾,无须扩容,LinkedList这个集合增删快,由于插入和删除元素,更少的读取数据 。由于插入和删除元素不涉及重排数据,因此它要比ArrayList要快。
线程的生命周期一共分为五个部分分别是:新建,就绪,运行,阻塞以及死亡。
生命周期的五种状态
1.新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)
Thread t1=new Thread();
2.就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。
3.运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
4.堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
5.死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
sleep()方法可以在任何地方使用,sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复。
wait()方法则只能在同步方法或同步块中使用,wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
**线程安全:**既然是线程安全问题,那么肯定是在多个线程访问的情况下产生的,没有按照我们预期的行为执行,那么线程就不安全了。也就是说我们想要确保在多线程访问的时候,我们的程序还能按照我们预期的行为去执行,那么就是线程安全。
线程同步和异步的区别:
线程同步是多个线程同时访问同一资源,等待资源访问结束,浪费时间,效率低 线程异步:访问资源时在空闲等待时同时访问其他资源,实现多线程机制。
异步处理就是你现在问我问题,我可以不回答你,等我用时间了再处理你这个问题。同步不就反之了,同步信息被立即处理直到信息处理完成才返回消息。
线程安全解决方案:常见的几种方式
不可变(final)
在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。final关键字修饰的类或数据不可修改,可靠性最高。如 String类,Integer类。
同步
悲观锁/乐观锁/锁优化
使用 synchronized 实现,这里的判断条件中用的是 while 而不是 if , 这两者之间有什么区别呢? 线程从 wait 状态被唤醒,并且获得锁以后会继续往下执行,比如 A 调用nofityAll() 唤醒 B,C,这时 B与C谁会先获得锁是不确定的。
如果是C先获得了锁,那么C就继续往下执行打印,这与我们的期望的不符。所以这里我们使用了一个 while,当C获得锁以后再去判断一下flag,如果这时还不是它执行的时候,它就再次进入wait状态。
此时A与C都是wait状态,获得锁的一定是B,从而实现我们期望的顺序打印。
先说HashMap的底层是数组+链表+红黑树
HashMap根据名称可知,其实现方法与Hash表有密切关系。
在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能。
**数组:**采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n)
线性链表:对于链表的新增,删除等操作(在找到指定操作位置后),仅需处理结点间的引用即可,时间复杂度为O(1),而查找操作需要遍历链表逐一进行比对,复杂度为O(n)
二叉树:对一棵相对平衡的有序二叉树,对其进行插入,查找,删除等操作,平均复杂度均为O(logn)。
HashMap实现原理
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
哈希冲突
哈希算法存在一个缺点就是哈希冲突。例如,我们进行数据存储时,我们通过对关键字进行hash时得到的地址已经存储过数据了,这时就会出现哈希冲突。因此,哈希函数的设计至关重要,好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀。但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。那么哈希冲突如何解决呢?哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。
一、抽象类
Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象类的特点
1、抽象类和抽象方法必须用abstract关键字来修饰。
2、抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符abstract返回值类型 函数名(参数列表);
3、 抽象类不可以被实例化,也就是不可以用new创建对象。原因如下:
抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。
而且抽象类即使创建了对象,调用抽象方法也没有意义。
4、抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
二、接口
接口,可以被认为是一个特殊的抽象类。当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。接口使用interface来表示,子类中用implements实现。格式为:
interface 接口名{}
子类名 implements接口名{}
格式特点:
1、接口中常见定义:常量,抽象方法。
2、接口中的成员都有固定修饰符。
常量:public static final
方法:public abstract
3、接口中的成员都是public的。
在使用中,常量可以不写publicstatic final,方法可以不写publicabstract,编译时Java会自动添加这些修饰符,因为这是固定的格式修饰符。但为了方便阅读,通常都写上。
接口的特点
1、接口是对外暴露的规则。
2、接口是程序的功能扩展。
3、接口的出现降低耦合性。
4、接口可以用来多实现。这也是对多继承不支持的转换形式。java支持多实现。
5、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
6、 接口与接口之间可以有继承关系。而且可以多继承。
注:1、接口不可以创建对象的,因为有抽象方法。需要被子类实现(implements),子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
2、实现多个接口时,接口中不可以有返回不同类型的同名抽象函数。这样子类实现时将不能复写。
三、接口与抽象类
共性:都是不断向上抽取出来的抽象的概念。
区别:
1、抽象类体现继承关系,一个类只能单继承。
接口体现实现关系,一个类可以多实现。同时接口与接口之间有继承关系。
2、抽象类是继承,是 "is a "关系。
接口是实现,是 "like a"关系。
3、抽象类中可以定义非抽象方法,供子类直接使用。
接口的方法都是抽象,接口中的成员都有固定修饰符。
4、抽象类中可以私有变量或方法。
接口中的常量和方法都是public修饰的权限。
1.原子性
事务要么全部成功,要么失败回滚
2.一致性
一个事务在执行前和执行后都处于一致性的状态
3.隔离性
多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
4.持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
前端发送请求到后端,后端的中央控制器(dispacherserlvet)接受到以后,调用处理器映射器(handleMapping)进行映射得知由哪个controller执行但是不去执行,中央控制器调用处理器适配器(handleAdapt)来执行具体的controller,后台控制器再返回给一个modelview,层层返回给前端控制器(dispacherservlet),控制器对模型数据用视图解析器进行解析(viewresolver),随后填充到用户访问的界面。
1.GET把参数包含在URL中,POST通过request body传递参数。
2.GET请求会被浏览器主动cache(缓存),而POST不会,除非手动设置。
3.GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
动态sql就是(在进行sql操作的时候)动态的根据属性值(所匹配的条件)来拼接数据库执行的sql语句,也就是多次查询或变更操作,根据传入的属性值不同,动态拼接出不同的可执行sql。包含判断为空、循环等;
< if >
if是为了判断传入的值是否符合某种规则,比如是否不为空;
< where >:
where标签可以用来做动态拼接查询条件,当和if标签配合的时候,不用显示的声明类似where 1=1这种无用的条件;
< choose >< when >< otherwise >:
这是一组组合标签,他们的作用类似于 Java 中的 switch、case、default。只有一个条件生效,也就是只执行满足的条件 when,没有满足的条件就执行
otherwise,表示默认条件;
< foreach >:
foreach标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到sql语句中,里面涉及到 item(具体的每一个对象), index(序号), open(开始符), close(结束符), separator(分隔符)
< include >
include可以把大量重复的代码整理起来,当使用的时候直接include即可,减少重复代码的编写
#{ }可以防止Sql 注入,它会将所有传入的参数作为一个字符串来处理。
$ {} 则将传入的参数拼接到Sql上去执行,一般用于表名和字段名参数,$ 所对应的参数应该由服务器端提供,前端可以用参数进行选择,避免 Sql 注入的风险。
详细解释: dollar符是将传入的数据直接显示生成sql语句,select id,name,age from student where id =${id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1
MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和JavaPOJO
优化子查询
使用索引
单值索引:一个索引只包含单个列,一个表可以由多个单值索引;
唯一索引:索引列的值必须唯一,允许有空值;
复合索引:即一个索引包含多个列;
主键索引(PRIMARY KEY)
唯一索引 (UNIQUE Indexs)
普通索引 (Normal index)
**普通索引(index):**最基本的索引,没有任何限制。如果是char,varchar类型,length可以小于字段长度。
第一种 like语句操作
第二种 使用复核索引
第三种 不要在列上进行运算
select * from users where YEAR(adddate)<2007;
将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成、select * from users where adddate<‘2007-01-01’;
第四种 尽量用union all代替union
union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。
Spring是一个开源框架,它是为了解决企业应用开发的复杂性而创建的。
基本概念:spring是容器框架,创建bean,维护bean之间的关系
spring可以管理,持久层,业务层,dao层,spring可以配置各个层的组件,并且维护各个层的关系。
Spring能用来做什么的?
简单来说:
第一IOC控制反转。Spring就是一个容器,用来封装JavaBean的。
第二AOP切面。切面利用动态代理实现面向切面编
使用spring ,没有new对象,我们把创建对象的任务交给spring框架。
程。
**spring工作原理:**通过BeanFactory(“applicationContext.xml”)等方式启动容器。在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例
Bean的生命周期:
使用Spring时,通过Spring注入的Bean一般都被定义成private,并且要有getter和setter方法,显得比较繁琐,增加了代码量,而且有时会搞忘造成错误。可以使用@Autowired注解来减少代码量。首先,在applicationContext中加入。
区别:
@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
@Autowired默认按类型装配(这个注解属于Spring),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false)
*@Resource(**这个注解属于J2EE),默认按照名称进行装配,名称可以通过name属性进行指定,
如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。如果name属性一旦指定,就只会按照名称进行装配。
等等符号 是java提供的等于比较运算符,用来比较两个变量指向的内存地址是否相同。
举列:如果是基本数据类型比值,或是引用数据类型话比的是内存地址
而equals()是Object提供的一个方法,引用数据重写话此方法就比值字面值,没有重写就用==比内存地址;
总体上来说分为两大部分:
首先启动类上注解:@SpringBootApplication
其次启动类中的main方法:org.springframework.boot.SpringApplication
1.查看启动类上的注解源码中,有三个核心注解:@SpringBootConfiguration
该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别。
@EnableAutoConfiguration
开启springboot的注解功能, @EnableAutoConfiguration借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IOC容器,
@ComponentScan
扫描的 Spring 对应的组件,如 @Componet,@Repository。
@SpringBootApplication
里面主要包含了@Configuration @EnableAutoConfiguration @ComponentScan
相关注解 | 说明 |
---|---|
@ComponentScan | 用来自动扫描被这些注解标识的类,最终生成ioc容器里的bean,默认扫描范围是@ComponentScan注解所在配置类包及子包的类 |
@SpringBootConfiguration | 与@Configuration作用相同,都是用来声明当前类是一个配置类,这里表明是springboot主类使用的配置类 |
@EnableAutoConfiguration | 是springboot实现自动化配置的核心注解,通过这个注解把spring应用所需的bean注入容器中 |
注解 | 说明 |
---|---|
@RequestMapping | @RequestMapping(url),通过该注解就可以通过配置的url进行访问,方式可以是get或post请求,两种方式均可 |
GetMapping | @GetMapping(url) ,功能类似的,只是这个限定了只能是Get请求 |
@PostMapping | @PostMapping(url),功能类似的,只是这个限定了只能是Post请求 |
@RequestMapping @GetMapping @PostMapping | |
这3个注解功能也是类似的,通过这3个注解来映射请求,也就是通过它来指定控制器可以处理哪些URL请求,用在方法上,可以通过配置的url进行访问 |
@Component @Service @Controller @Repository
这几个注解放在一起是因为功能基本一样的,都是将类注入到spring容器中,只不过它们使用的场景不同,被@Component,@Service,@Controller,@Repository注解标注的类,这些类会被纳入进spring容器中管理。
注解 | 说明 |
---|---|
@Repository | 持久层(dao)注入spring容器 |
@Service | 业务逻辑层(server)注入spring容器 |
@Controller | 控制层(controller)注入spring容器 |
@Component | 普通pojo注入spring容器 |
注解 | 说明 |
---|---|
@ResponseBodyy | @ResponseBody的作用其实是将java对象转为json格式的数据。 |
@ResponseBody | |
@ResponseBody可以作用在方法上或类上,表示该方法的返回结果直接写入 HTTP response body 中,而不会被解析为跳转路径,即不会经过视图解析器,返回什么数据即在页面输入什么数据。 |
@RestController
该注解是@Controller和@ResponseBody的结合体,一般用于类,作用等于在类上面添加了@ResponseBody和@Controller
@AutoWired
@Configuration&@Bean
@Configuration作用于类上面,表明这是一个配置类,@Bean产生一个Bean对象加入Spring IOC容器。注意:@Configuration标注在类上,相当于把该类作为spring的xml配置文件中,作用为:配置spring容器(应用上下文)
注解 | 说明 |
---|---|
@Configuration | 作用于类上表示这是一个配置类,可理解为用spring的时候xml里面的< beans>标签 |
@Bean | 产生bean对象加入容器,作用于方法,可理解为用spring的时候xml里面的标签 |
@PathVariable | |
注解 | 说明 |
-------- | ----- |
@PathVariable | 获取路径参数。即url/{id}这种形式。 |
bootstrap.yml文件也是SpringBoot的默认配置文件,而且其加载的时间相比于application.yml更早。
bootstrap.yml可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。application.yml 可以用来定义应用级别的参数,如果搭配 spring cloud config 使用,application.yml 里面定义的文件可以实现动态替换。
总结就是,bootstrap.yml文件相当于项目启动时的引导文件,内容相对固定。application.yml文件是微服务的一些常规配置参数,变化比较频繁。
一个char 类型占 2 个字节(16 比特),所以放一个中文是没问题的
ClassCastException类转换异常
IllegalArgumentException非法参数异常
IndexOutOfBoundsException数组越界异常
NullPointerException空指针异常
ArrayStoreException数据存储异常
1、Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
2、Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
Collections 是一个包装类,Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许,一些 collection 是有序的,而另一些则是无序的。
工厂模式 单例模式
在SSM框架中,使用的是Spring的事务管理机制。Spring可以使用编程式实现事务, 声明式 实现事务以及注解式实现事务。
Spring 是一个容器,不同的框架在处理事务时用到的对象不同,Mybatis 中使用 SqlSession 对象。而 Spring 为了整合这些不同的框架,定义了一个 PlatformTransactionManager 接口来统一标准,对不同的框架又有不同的实现类。
在 Spring 中根据 DAO 层技术的不同来选择不同的事务处理的对象,是 JDBC 时使用 DataSourceTransactionManager,是 Hibernate 时使用 HibernateTransitionmanager 对象,核心对象就是 Transitionmanager。
在 Spring 中管理事务会涉及到这几个属性,事务隔离级别、是否只读、事务的传播行为,说到事务的传播行为,指的就是不同的业务方法之间相互调用时,应该如何管理事务。Spring 中一共定义了 7 种传播行为,无脑记住使用 required ,表示支持当前事务,若是不存在事务,就创建一个。例如在 A 调用 B 的时候,会首先使用 A 的事务,若 A 没有事务,则新创建一个,不管 B 有没有事务。
主要以为xml配置和注解配置为主。
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查)。将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用)
关于线程同步———7种方式
1.同步方法
即有synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
代码如:
public synchronized void save(){}
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
2.使用局部变量实现线程同步
StringBuffer多线程安全,但是加了synchronized,其效率低,故适用于多线程下,并发量不是很高的场景。
StringBuilder没有加任何锁,其效率高,适用单线程场景,但同时也适用于高并发场景中,提高高并发场景下程序的响应性能,至于线程安全问题可以通过其它手段解决,如ThreadLocal,CAS操作等。
所以对于高并发场景下,若有用到二者,还是建议优先使用StringBuilder
String是final修饰的类,是不可变的,所以是线程安全的。
拓展:Java String类为什么是final的?
1.为了实现字符串池
2.为了线程安全
3.为了实现String可以创建HashCode不可变性
Bean生命周期一般有下面的四个阶段:
Bean的定义
Bean的初始化
Bean的生存期
Bean的销毁
Bean的定义过程:
第一步,资源定位,就是Spring根据我们定义的注解(@Component),找到相应的类。
找到了资源就开始解析,并将定义的信息保存起来,此时,并没有初始化bean,这点需要注意。
然后将bean的定义发布到SpringIoc的容器中,此时,SpringIoc的容器中还是没有Bean的生成。只是定义的信息。
Bean的初始化:
经过Bean的定义,初始化,Spring会继续完成Bean的实例化和依赖注入,这样从IoC容器中就可以得到一个依赖注入完成的Bean。
Spring初始化Bean
Bean的生命周期:
1.volatile是变量修饰符,而synchronized则作用于一段代码或方法。
2.volatile只是在线程内存和“主”内存间同步某个变量的值;而synchronized通过锁定和解锁某个监视器同步所有变量的值, 显然synchronized要比volatile消耗更多资源。
3.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
4.volatile保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存中和公共内存中的数据做同步。
1.LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。ArrayList是基于数组实现的,LinkedList是基于双链表实现的。另外LinkedList类不仅是List接口的实现类,可以根据索引来随机访问集合中的元素,除此之外,LinkedList还实现了Deque接口,Deque接口是Queue接口的子接口,它代表一个双向队列,因此LinkedList可以作为双向队列 ,栈(可以参见Deque提供的接口方法)和List集合使用,功能强大。
2.因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。Array获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大的,因为这需要移动数组中插入位置之后的的所有元素。
3.相对于ArrayList,LinkedList的随机访问集合元素时性能较差,因为需要在双向列表中找到要index的位置,再返回;但在插入,删除操作是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O
ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
使用场景:
(1)如果应用程序对数据有较多的随机访问,ArrayList对象要优于LinkedList对象;
( 2 ) 如果应用程序有更多的插入或者删除操作,较少的随机访问,LinkedList对象要优于ArrayList对象;
401 gateway场景下常见,表示没有权限访问
403
404 访问路径的URL错误 检查网址上的路径
405 参数不匹配
500 后端请求参数错误
四种方式:
第一种——继承Thread类创建线程
1)定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2)创建Thread子类的实例,也就是创建了线程对象
3)启动线程,即调用线程的start()方法
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
第二种———实现Runnable接口创建线程
通过实现Runnable接口创建并启动线程一般步骤如下:
1)定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2)创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3)第三步依然是通过调用线程对象的start()方法来启动线程
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start()
//或者 new Thread(new MyThread2()).start();
}
}
SQL优化:
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’
7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=‘abc’–name以abc开头的id
应改为:
select id from t where name like ‘abc%’
9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
11.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)
12.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
失效的场景:
1.存在NULL值条件
我们在设计数据库表时,应该尽力避免NULL值出现,如果非要不可避免的要出现NULL值,也要给一个DEFAULT值,数值型可以给0、-1之类的, 字符串有时候给空串有问题,就给一个空格或其他。如果索引列是可空的,很可能是不会给其建索引的,索引值是少于表的count(*)值的,所以这种情况下,执行计划自然就去扫描全表了。
2.LIKE通配符
当使用模糊搜索时,尽量采用后置的通配符,例如:name||’%’,因为走索引时,其会从前去匹配索引列,这时候是可以找到的,如果采用前匹配,那么查索引就会很麻烦,比如查询所有姓张的人,就可以去搜索’张%’。
3.条件上包括函数
select * from test where upper(name)=‘SUNYANG’;
优点:
1.与JDBC相比,减少了50%以上的代码量。
2. MyBatis是最简单的持久化框架,小巧并且简单易学。
3. MyBatis灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,可重用。
4. 提供XML标签,支持编写动态SQL语句(XML中使用if, else)。
5. 提供映射标签,支持对象与数据库的ORM字段关系映射(在XML中配置映射关系,也可以使用注解)。
缺点:
7. SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
8. SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
单例模式(保证一个类只有一个实例,并且提供一个访问该全局访问点)
使用场景:网站的计数器,一般也是采用单例模式实现
观察者模式
适配器模式
将两种完全不同的事物联系到一起,就像现实生活中的变压器。
工厂模式
(工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。)
简单工厂模式:一个抽象的接口,多个抽象接口的实现类,一个工厂类,用来实例化抽象的接口
代理模式
通过代理控制对象的访问,可以在这个对象调用方法之前、调用方法之后去处理/添加新的功能。
1.静态代理:简单代理模式,是动态代理的理论基础。常见使用在代理模式
jdk动态代理:使用反射完成代理。需要有顶层接口才能使用,常见是mybatis的mapper文件是代理。
cglib动态代理:也是使用反射完成代理,可以直接代理类(jdk动态代理不行),使用字节码技术,不能对 fifinal类进行继承。(需要导入jar包)
策略模式
定义了一系列的算法 或 逻辑 或 相同意义的操作,并将每一个算法、逻辑、操作封装起来,而且使它们还可以相互替换。(其实策略模式Java中用的非常非常广泛)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。