(1) 自我介绍
(2) 你做过哪些项目?你负责什么
(3) 你在这个项目中有什么收获
(4) 你在项目中遇到问题怎么解决的
(5) 讲讲你的项目
(6) 你的项目是怎么开展的
JVM(Java Virtual Machine):
Java虚拟机是一个可以执行Java字节码的虚拟机进程。
Java源文件被编译成能被Java虚拟机执行的字节码文件。
用于保证Java的跨平台的特性。
JRE(Java Runtime Environment):
Java的运行环境,包括JVM+java的核心类库。
JDK(Java Development Kit):
Java的开发工具,包括JRE+开发工具,
可以让开发者开发、编译、执行Java应用程序。
path是配置Windows可执行文件的搜索路径,即扩展名为.exe的程序文件所在的目录, 用于指定DOS窗口命令的路径。
classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。
(根据个人理解,简要回答,言多必失)
GC是垃圾收集的意思,垃圾回收可以有效的防止内存泄露,有效的使用内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显式操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
变量的作用:用来存储数据。
为什么要定义变量:用来不断的存放同一类型的常量,并可以重复使用
基本数据类型(Primitive Type):byte,short,int,long,float,double,char,boolean
引用数据类型(Reference Type):类,接口,枚举,注解 ,数组
数据类型的相互转换
正向的过程: 从低字节向高字节可以自动转换 (Java中的自动类型提升 ) byte-> short-> int-> long-> float-> double
三种情况:
int -> float
long -> float
long -> double
特殊情况:char <-> int (相互转换)
& , | 也可以在算术运算中使用;
(exp1) & (exp2) : exp1,exp2 都要执行
(exp1) | (exp2) : exp1,exp2 都要执行
(exp1) && (exp2) 若exp1 表达式返回false, exp2不执行,直接返回结果false
(exp1) || (exp2) 若exp1 表达式返回true, exp2不执行,直接返回结果true
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
备注:后面一部分回答虽然不是在正面回答题目,但是,为了展现自己的学识和表现自己对问题理解的透彻深入,可以回答一些相关的知识,做到知无不言,言无不尽。
就近原则:
i++ 先赋值后自增,例如 a=i++,先赋值a=i,后自增i=i+1,所以结果是a=1
++i 先自增在赋值,例如 a=++i,先自增i=i+1,后赋值a=i,所以结果是a=2
对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
(1)修饰类:表示该类不能被继承,即不能有子类
(2)修饰方法:表示方法不能被重写;
(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
在Java 5以前,switch(expr)中,expr只能是byte、short、char、int,枚举。
从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
(1)Integer是int的包装类;int是基本数据类型;
(2)Integer变量必须实例化后才能使用;int变量不需要;
(3)Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
(4)Integer的默认值是null;int的默认值是0
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
------------------------------------------------------------------------------------------------
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
排序的方法:插入排序、**冒泡排序、快速排序、**选择排序、归并排序。
使用快速排序方法对a[0:n-1]排序:
从a[0:n-1]中选择一个元素作为middle,该元素为支点
把余下的元素分割为两端left和right,使得left中的元素都小于等于支点,而right中的元素都是大于支点。
递归的使用快速排序方法对left进行排序
递归的使用快速排序方法对right进行排序。
所得结果为left+middle+right
String类的一个最大特性是不可修改性,而导致其不可修改的原因是在String内部定义了一个常量数组final char数组,因此每次对字符串的操作实际上都会另外分配分配一个新的常量数组空间
· 初始化
public final class String implements java.io.Serializable, Comparable, CharSequence {
//数组定义为常量,不可修改
private final char value[];
public String() {
this.value = new char[0];
}
· 实例化字符串
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
根据个人理解,能讲多少是多少
面对对象就是:
把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。
对象即为人对各种具体物体抽象后的一个概念,人们每天都要接触各种各样的对象,如手机就是一个对象。
在面向对象的编程方式中,对象拥有多种特性,如手机有高度、宽度、厚度、颜色、重量等特性,这些特性被称为对象的属性。对象还有很多功能,如手机可以听音乐、打电话、发信息、看电影等工作功能,这些功能被称为对象的方法,实际上这些方法是一种函数。而对象又不是孤立的,是有父子关系的,如手机属于电子产品,电子产品属于物体等,这种父子关系称为对象的继承性。在编程中,把所有的问题看做对象,以人的思维方式解决。这种方式非常人性化,对象实际上就是一组数据的集合,并且数据都已经命名。这些数据根据就是对象的属性,可以被程序访问。对象还包括很多函数,这些函数被称为对象的方法,也可以被程序访问。不过在外部访问对象内的属性或方法,必须先引用对象,然后用点号访问对象的属性和方法
(1)封装
概念:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:将变化隔离;便于使用;提高重用性;安全性。
封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。
(2)继承
好处:a.提高了代码的复用性。
b.让类与类之间产生了关系,提供了另一个特征多态的前提。
(3)多态
体现:父类引用或者接口的引用指向了自己的子类对象。//Animal a = new Cat();
多态的好处:提高了程序的扩展性。
多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
多态的前提:
a. 必须要有关系,比如继承、或者实现。
b. 通常会有重写操作。
public:使用public修饰,对所有类可以访问。
protected:使用protected修饰,对同一包内和所有子类可以访问。
缺省:不使用任何修饰符,在同一包内可以访问。
private:使用private修饰,在同一类内可以访问。
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载发生在一个类中,同名的方法如果有不同的参数列表
(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重写
(1)在继承关系中;
(2)、修饰符:(子类的不能比父类更严格)子类中的修饰符只能和父类中方法的修饰符相同或者更宽;
(3)、返回值:子类中的方法的返回值只能是父类中方法返回值类型的子类或者相同; 对于基本类型必须完全一致;
(4)、方法名:必须相同;
(5)、参数列表:必须相同;
(6)、异常:子类中方法抛出异常只能是父类中方法抛出异常的子类或者相同。 子类中的方法可以不抛出任何异常。
应用方面的区别:
接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。
抽象类在代码实现方面发挥作用,可以实现代码的重用。
(理解示例,模板方法设计模式是抽象类的一个典型应用,假设某个项目的所有Servlet类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的Servlet都继承这个抽象基类,在抽象基类的service方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码。)
语法方面的区别:
(1)抽象类和接口都不能直接实例化,如果要实例化,抽象类对象必须指向实现所有抽象方法的子类对象, 接口对象必须指向实现所有接口方法的类对象。
(2)抽象类要被子类继承,接口要被类实现。
(3)接口中只有全局常量与抽象方法;
抽象类中可以有抽象方法与具体方法实现,可以有类变量与实例变量
(4)接口可继承接口,并可多继承接口;但抽象类只能单继承
不支持,Java 不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,
而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,
也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
String: 字符串不可变,适用于字符串内容不经常发生改变的时候
StringBuilder: 字符串可变,适用于字符串内容经常发生改变的时候,适用于单线程(线程不安全),在单线程中,执行效率较高
StringBuffer:字符串可变,适用于字符串内容经常发生改变的时候,适用于多线程(线程安全)
(讲到此处就可以,以下是理解)
详细分析:
String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做
在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同.
indexOf()返回指定字符串在该字符串出现的序列
startsWith()判断该字符串是否以指定字符开始
concat() 将指定字符串连接在该字符串的结尾
length()返回字符串的长度
subString返回该字符串从索引开始 结束于字符串末尾 或者指定索引的一个子字符串
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
Error和Exception都是继承于Throwable。
Error一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
a) finally: 用于释放资源,节省内存,语法上无论是否有异常,都会执行
b) return : 方法结束
c) 先执行return ,再执行finally
若返回值类型是:基本数据类型,finally 中修改返回值变量,返回值不会改变【值传递】
若返回值类型是:引用数据类型,finally 中修改返回值变量,返回值会改变【引用传递】
ArithmeticException
ClassCastException
IllegalArgumentException
IndexOutOfBoundsException
NullPointerException
SecurityException
final:修饰符有三种用法:
(1)修饰类:表示该类不能被继承,即不能有子类
(2)修饰方法:表示方法不能被重写;
(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
finally:用在异常结构中
通常放在try…catch的后面构造总是执行代码块,可以将释放外部资源的代码写在finally块中,即finally的作用是释放资源,节省内存。
finalize:Object类中GC相关的方法
Object类中的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。
(1)基本数据类型比较,用双等号(==),比较的是他们的值。
(2)引用数据类型比较,
a. 使用==比较,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,比较后的结果为true,否则结果为false。( JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。–这部分帮助理解)
b. 使用equals比较,若没有重写Object类的equals方法,比较还是内存地址(==理解);
若重写Object类的equals方法,equals比较的是堆中内容是否相等,即两个对象的内容是否相同。
装箱:将基本类型—转换成—对应的包装类;
拆箱:将包装类型—转换成—基本数据类型;
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率,由编译器来完成,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。
按值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
按引用传递:引用传递就是直接把内存地址传过去,操作的其实都是源数据,这样的话修改有时候会冲突,记得用逻辑弥补下就好了。具体的数据类型就比较多了,比如Object,二维数组,List,Map等除了基本类型的参数都是引用传递。
反射是指在运行时能查看一个类的状态及特征,并能进行动态管理的功能。这些功能是通过一些内建类的反射API提供的,比如Class,Method,Field, Constructors等。使用的例子:使用Java反射API的getName方法可以获取到类名。
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。
双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成 用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
在我们日常的工作中经常需要在应用程序中保持一个唯一的实例,如:IO处理,数据库操作等,由于这些对象都要占用重要的系统资源,所以我们必须限制这些实例的创建或始终使用一个公用的实例,这就是单例模式(Singleton)。
/第一种生成singleton优点:简单,缺点:生成的资源浪费/
class singleton1{
private singleton1(){};
private static singleton1 s1=new singleton1();
public static singleton1 getSingleton(){
return s1;
}
}
/第二种优点:不占用资源,缺点:不是完全的线程安全,如果在if语句执行过后转移到另一线程执行,在转移回来的时候将不进行判断,又生成一次/
class singleton2{
private singleton2(){
}
private static singleton2 s2=null;
public static synchronized singleton2 getSingleton(){
if(s2==null)
s2=new singleton2();
return s2;
}
(1)多线程安全单例模式实例一(不使用同步锁)
public class Singleton {
private static Singleton sin=new Singleton(); ///直接初始化一个实例对象
private Singleton(){ ///private类型的构造函数,保证其他类对象不能直接new一个该对象的实例
}
public static Singleton getSin(){ ///该类唯一的一个public方法
return sin;
}
}
上述代码中的一个缺点是该类加载的时候就会直接new 一个静态对象出来,当系统中这样的类较多时,会使得启动速度变慢 。现在流行的设计都是讲**“延迟加载”**,我们可以在第一次使用的时候才初始化第一个该类对象。所以这种适合在小系统。
(2)多线程安全单例模式实例二(使用同步方法)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance(){ //对获取实例的方法进行同步
if (instance == null)
instance = new Singleton();
return instance;
}
}
上述代码中的一次锁住了一个方法, 这个粒度有点大 ,改进就是只锁住其中的new语句就OK。就是所谓的“双重锁”机制。
(3)多线程安全单例模式实例三(使用双重同步锁)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance(){ //对获取实例的方法进行同步
if (instance == null){
synchronized(Singleton.class){
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
bit(位)最小的二进制单位 ,是计算机的操作部分 取值0或者1
byte(字节)是计算机操作数据的最小单位由8位bit组成 取值(-128-127)
char(字符)是用户的可读写的最小单位,在Java里面由16位bit组成 取值(0-65535)
首先来讲一下传统的IO和NIO的区别,传统的IO又称BIO,即阻塞式IO,NIO就是非阻塞IO了。还有一种AIO就是异步IO,这里不加阐述了。
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
UTF-8 (8-bit Unicode Transformation Format) 是一种针对Unicode的可变长度字符编码,又称万国码,它包含全世界所有国家需要用到的字符,是国际编码,通用性强,是用以解决国际上字符的一种多字节编码。UTF-8用1到4个字节编码UNICODE字符,它对英文使用8位/8Bit(即1个字节/1Byte),中文使用24位/24Bit(3个字节/3Byte)来编码。用在网页上可以同一页面显示中文简体繁体及其它语言(如日文,韩文)。
GBK (Chinese Internal Code Specification) 是汉字编码标准之一,全称《汉字内码扩展规范》,GBK是国家标准GB2312基础上扩容后兼容GB2312的标准(GB2312共收录了7445个字符,包括6763个汉字和682个其它符号;GBK共收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字)。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示(注意,GB系列编码是利用了字节中的最高位和ASCII编码区分的,可以和ASCII码混用。所以全角模式下英文是2字节,半角模式英文还是1字节)。为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBD大。
简单概况就是:
UTF-8英文1字节中文3字节,在编码效率和编码安全性之间做了平衡,适合网络传输,是理想的中文编码方式.
GBK英文1字节(半角1字节,全角2字节),中文2字节,GBK的范围比GB2312广,GBK兼容GB2312。
字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件
二. 在硬盘上的所有文件都是以字节形式存在的(图片,声音,视频),而字符值在内存中才会形成。
要把一片二进制数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream ,不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的。
在应用中,经常要完成是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换成字符。由于这样的需求很广泛,人家专门提供了字符流的包装类。
底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设备写入或读取字符串提供了一些方便。
在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。
有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。
sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
join():指等待t线程终止。
yield():暂停当前正在执行的线程对象,执行具有相同或更高优先级其他线程。
Run()
Start()
Wait()
Notify()
sleep()方法给其它线程运行时,不考虑线程的优先级;而yield()方法只会给相同优先级或更高优先级的线程运行的机会。
执行sleep()方法后会转入阻塞状态,所以线程在指定的时间内肯定不会被执行;而yield()只是使当前线程重新回到可执行状态,所以线程有可能在进入到可执行状态后马上又被执行。
sleep()方法声明抛出InterruptedException;而yield()没有声明任何异常。
sleep()方法比yield()方法具有更好的可移植性。
同步:所有的操作都做完,才返回给用户结果。即写完数据库之后,在相应用户,用户体验不好
异步:不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好
同步交互:发送 一个请求,需要等待响应返回,然后才能够发送下一个请求,有个等待的过程。
异步交互:发送 一个请求,不需要等待响应返回,随时可以再发送另一个请求,即不需要等待。
JVM启动时会有一个由主方法所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
Start()让线程进入就绪状态
Run() 是线程正在运行状态
(1)同步方法
即有synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
(2)同步代码块
即有synchronized关键字修饰的语句块。 被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
代码如:
synchronized(object){
}
(3**)**wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
(4)使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
同步用来控制共享资源在多个线程间的访问,以保证同一时间内只有一个线程能访问到这个资源。在非同步保护的多线程程序里面,一个线程正在修改一个共享变量的时候,可能有另一个线程也在使用或者更新它的值。同步避免了脏数据的产生。
线程安全:多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。(Vector,Hashtable,StringBuffer)
线程不安全:不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。(ArrayList,LinkedList,HashMap,StringBuilder等)
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
**集合框架:**用于存储数据的容器
1:对象封装数据,对象也需要存储。【集合用于存储对象】
2:对象的个数确定可以使用数组,但是不确定怎么办?可以用集合。【因为集合是可变长度的】
1:数组是固定长度的;集合可变长度的。
2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。
Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。
常用实现子类:HashSet,TreeSet
List是一个可以包含重复元素的集合。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。
常用实现子类:ArrayList,LinkedList
Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
(1) ArrayList本质是数组 ,线程不同步,ArrayList替代了Vector,查询元素的速度非常快,默认大小10,1.5倍长度扩容
- 频繁的添加操作:可能会造成扩容,内存浪费与大量元素复制移位,随着数据量增多,时空复杂度会增大;
- 频繁的删除操作:可能会出现大量元素的移位
- 频繁的取元素操作:能直接根据数组下标定位元素,因为数据在数组中的物理地址是连续的;
- 【所以:ArrayList 适用于元素的随机访问】
(2) LinkedList 本质是链表,线程不同步,增删元素的速度非常快
- 频繁的添加操作:只需要一个元素内存消耗
- 频繁的删除操作:只需要元素指针重新连接
- 频繁的取元素操作:需要遍历整个链表,因为链表的物理地址是不连续的
- 【所以:LinkedList适用于元素的大量添加与删除】
(3) Vector:底层的数据结构就是数组,线程同步,Vector无论查询和增删都巨慢。默认大小10,2倍长度扩容。
TreeSet 是二叉树实现的,Treeset中的数据是要求泛型对象实现自然顺序或自定义排序,不允许放入null值。
HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。
HashSet要求放入的对象重写equals()、hashCode()方法,对象放入集合中首先是以hashcode码作为标识。
TreeSet判断两个对象不相等的方式是通过排序实现
(1)自然排序
自然排序要实现Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2),若返回0,则说明被比较的两个对象相等,若返回一个正数,则表明obj1大于obj2,若返回负数,则表明obj1小于obj2。
(2)自定义排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。
Hashtable
底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个Hashtable,效率低,ConcurrentHashMap做了相关优化
初始size为11,扩容:newsize = olesize*2+1
计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length
HashMap
底层数组+链表实现(JDK7),可以存储null键和null值,线程不安全
初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
计算index方法:index = hash & (tab.length – 1)
1、HashMap在JDK7中的数据结构主要是:数组+链表,数组和链表的节点的实现类时Entry类
HashMap在JDK8中的数据结构主要是:数组+链表+红黑树,当链表的长度大于8的时候,后面的数据结构转化为红黑树,数组和链表的节点的实现类时Node类
2、Hash值的计算
JDK7:h^ =(h>>>20)^(h>>>12);
return h^ (h>>>7)^(h>>>4);
JDK8:(key==null)?0:(h=key.hashCode())^(h>>>16)
3、链表数据插入的区别
JDK7:使用的头插入法,扩容后与原位置相反,防止尾部遍历
JDK8:使用的头插法,由于使用了红黑树,构造红黑树有个过程,扩容后位置与原链表相同
因为HashMap在JDK7中是用的链表的纵向延伸,采用头插法容易造成死循环;JDK8中加入了红黑树,避免了这一问题
4、内部类Entry
JDK7中有一个内部类Entry,实现了Map.Entry接口
JDK8中变成了Node类,也实现了Map.Entry接口
5、红黑树、二叉树、平衡二叉树
查询效率:平衡二叉树 > 红黑树 > 二叉树
构造树的开销:二叉树 < 红黑树 < 平衡二叉树
所以JDK8选择了折中的红黑树
6、使用HashMap的注意点
key如果是对象,一定要重写hashCode()和equals()方法;
创建时指定初始大小,避免扩容影响效率(元素个数 / 扩容因子)+1;
HashMap的默认值大小为16,扩容因子0.75;
HashMap的元素个数超过8的时候才会转换成红黑树
(1) 为什么要重写equals方法
Java中的集合(Collection)有两类,一类是List,再有一类是Set。 前者集合内的元素可以重复;后者元素不可重复。
那么我们怎么判断两个元素是否重复呢? 这就是Object.equals方法了。
通常想查找一个集合中是否包含某个对象,就是逐一取出每个元素与要查找的元素进行比较,
当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则返回否定的信息。
如果一个集合中有很多元素譬如成千上万的元素,并且没有包含要查找的对象时,
则意味着你的程序需要从该集合中取出成千上万个元素进行逐一比较才能得到结论,
于是,有人就发明了一种【【哈希算法来提高从集合中查找元素的效率】】,这种方式
将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,
每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域.
(2) 为什么要重写hashCode方法
hashCode方法返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,
先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,
不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次
总结:
先通过hashcode方法返回的值,直接定位元素应该存储的物理位置,位置上没有元素则存放,若有元素,再比较对象的equals方法,从而大大减少equals方法调用,提高比较效率。 (面试先用这一句话总结)
Iterator接口提供遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者在迭代过程中移除元素
Collection 是Java集合的根接口,下面有List,Set等子接口
Collections 是集合的工具类,提供了对集合进行排序,查询等方法。
用到了数据存储,信息配置两方面。
在做数据交换平台时,将数据源中的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。
两种形式 DTD,Schema,
区别:
l DTD无法解读扩充的内容,DTD只能把文件类型定义为一个字符串解析
XML Schema却允许把文件类型定义为整数,浮点数,字符串,布尔值或其他各数据类型,而无须重新定义
l Schema利用命名空间将文件中特殊的节点与Schema说明相联系,一个XML文件可以有多个对应的Schema;而一个XML文件只能有一个相对应的DTD文件.
l Schema是标准的XML文件,而DTD则使用自己的特殊语法,因此,只需要知道XML的语法规则就可以编写Schema了,不需要在学习其他语法规则.
有DOM,SAX,STAX等
l DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
l SAX:不限于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问 STAX:Streaming API for XML (StAX)
加载数据库驱动
获取数据库连接Connection;
创建Statement/PreparedStatement对象;
执行SQL语句;
获得结果集ResultSet,数据封装处理等;
回收数据库资源,包括关闭ResultSet, PreparedStatement Connection
Statement是父接口,PreparedStatement,CallableStatement是子接口。
PreparedStatement是预编译的SQL语句,效率高于Statement。
PreparedStatement支持?占位符操作符,相对于Statement更加灵活。
PreparedStatement可以防止SQL注入,安全性高于Statement。
CallableStatement适用于执行存储过程。
工作原理:
JAVA EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。
实现方案:连接池使用集合来进行装载,返回的Connection是原始Connection的代理,代理Connection的close方法,当调用close方法时,不是真正关连接,而是把它代理的Connection对象放回到连接池中,等待下一次重复利用。
数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:
最小连接数是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费;
最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。
如果最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。
mysql语法:
SELECT *
FROM 表名
LIMIT [START], length;
oracle语法:
SELECT *FROM (
SELECT 列名,列名,ROWNUM rn
FROM 表名
WHERE ROWNUM<=(currentPage*lineSize)) temp
WHERE temp.rn>(currentPage-1)*lineSize;
事务是业务上的一个逻辑单元,它能够保证其中对数据所有的操作,要么成功,要么不执行。
一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性。 Connection类中提供了4个事务处理方法:
setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动提交,即为true,通过设置false禁止自动提交事务;
commit():提交事务;
rollback():回滚事务.
savepoint:保存点
注意:savepoint不会结束当前事务,普通提交和回滚都会结束当前事务的
l DML(Data Manipulation Language)数据操纵语言:
如:SELECT、UPDATE、INSERT、DELETE。 主要用来对数据库的数据进行一些操作。
l DDL(data definition language)数据库定义语言:
如:CREATE、ALTER、DROP等。DDL主要是用在定义或改变表的结构,数据类型,表之间的链接和约束等初始化工作上。
l DCL(Data Control Language)数据库控制语言:
是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。
SQL(Structured Query Language)结构化查询语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。同时也是数据库脚本文件的扩展名。
索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库。
MySQL数据库几个基本的索引类型:普通索引、唯一索引、主键索引、全文索引
· 索引加快数据库的检索速度
· 索引降低了插入、删除、修改等维护任务的速度
· 唯一索引可以确保每一行数据的唯一性
· 通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能
· 索引需要占物理和数据空间
通常,通过索引查询数据比全表扫描要快.但是我们也必须注意到它的代价.
索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改. 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O. 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
· 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
· 基于非唯一性索引的检索
·
事务,是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
.事务四大特性
原子性,要么执行,要么不执行
隔离性,所有操作全部执行完以前,其它会话不能看到过程
一致性,事务前后,数据总额一致
持久性,一旦事务提交,对数据的改变就是永久的
MYISAM 不支持事务,不支持外键,表锁,插入数据时,锁定整个表,查表总行数时,不需要全表扫描
INNODB 支持事务,支持外键,行锁,查表总行数时,全表扫描
数据库的乐观锁和悲观锁是什么?
数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
· 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
· 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
SQL中的drop、delete、truncate都表示删除,但是三者有一些差别
· delete和truncate只删除表的数据不删除表的结构
· 速度,一般来说: drop> truncate >delete
· delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;
如果有相应的trigger,执行的时候将被触发. truncate,drop是ddl, 操作立即生效,原数据不放到rollback segment中,不能回滚. 操作不触发trigger.
· 不再需要一张表的时候,用drop
· 想删除部分数据行时候,用delete,并且带上where子句
· 保留表而删除所有数据的时候用truncate
第一范式:原子性,表中每个字段都不能再分。
第二范式:满足第一范式并且表中的非主键字段都依赖于主键字段。
第三范式:满足第二范式并且表中的非主键字段必须不传递依赖于主键字段。
存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
存储过程是用户定义的一系列sql语句的集合,涉及特定表或者其他对象的任务,用户可以调用存储过程,
函数通常是数据库已定义的方法,它接受参数并返回某种类型的值并且不涉及特定用户表。
事前触发运行于触发事件发生之前,而事后触发运行于触发事件发生之后。通常事前触发器可以获取事件之前和新的字段值。
语句级触发可以在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。
MyISAM:
不支持事务,但是每次查询都是原子的;
支持表级锁,即每次操作是对整个表加锁;
存储表的总行数;
一个MYISAM表有三个文件:索引文件、表结构文件、数据文件;
采用菲聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
InnoDb:
支持ACID的事务,支持事务的四种隔离级别;
支持行级锁及外键约束:因此可以支持写并发;
不存储总行数;
一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。
关系型数据库(MySQL、Oracle、SQL Server)
表和表、表和字段、数据和数据存在着关系
优点:
1.数据之间有关系,进行数据的增删改查的时候是非常方便的
2.关系型数据库是有事务操作的,保证数据的完整性和一致性。
缺点:
1.因为数据和数据是有关系的,底层是运行了大量的算法
大量算法会降低系统的效率,会降低性能
2.面对海量数据的增删改查的时候会显的无能为力
3.海量数据对数据进行维护变得非常的无力
非关系数据库的(redis和MangDB)
为了处理海量数据,非关系数据库设计之初就是为了替代关系型数据库的关系
优点:
1.海量数据的增删改查是可以的
2.海量数据的维护和处理非常轻松
缺点:
1.数据和数据没有关系,他们之间就是单独存在的
2.非关系数据库没有关系,没有强大的事务关系,没有保证数据的完整性和安全性
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
select * from table limit start,length 【a:从第几条记录开始,b表示查询出几条记录】
分页需求:
客户端通过传递start(页码),limit(每页显示的条数)两个参数去分页查询数据库表中的数据,那我们知道MySql数据库提供了分页的函数limit m,n,但是该函数的用法和我们的需求不一样,所以就需要我们根据实际情况去改写适合我们自己的分页语句,具体的分析如下:
比如:
查询第1条到第10条的数据的sql是:select * from table limit 0,10; ->对应我们的需求就是查询第一页的数据:select * from table limit (1-1)*10,10;
查询第10条到第20条的数据的sql是:select * from table limit 10,20; ->对应我们的需求就是查询第二页的数据:select * from table limit (2-1)*10,10;
查询第20条到第30条的数据的sql是:select * from table limit 20,30; ->对应我们的需求就是查询第三页的数据:select * from table limit (3-1)*10,10;
一、使用索引
无需一开始就扫描整个表,添加必要的索引由此显著地加快查询速度。
二、尽量避免使用select *,返回无用的字段会降低查询效率
三、尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE id IN (2,3)
SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)
优化方式:如果是连续数值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
如果是子查询,可以用exists代替。如下:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)
四、使用连接(JOIN)来代替子查询(Sub-Queries)
是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
五、使用联合(UNION)来代替手动创建的临时表
六、优化的查询语句
首先,最好是在相同类型的字段间进行比较的操作。
其次,在建有索引的字段上尽量不要使用函数进行操作。
第三,在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。
最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。
学生S(SND,SNAME,SA,SD)
课程C(CNO,CN,TNAME),
选课SC(SNO,CNO,G)
其中SNO代表学号,SNAME代表学生姓名,SA代表学生年龄,SD代表学生所在系,CNO代表课程号,CN代表课程名,TNAME代表任课老师姓名,G代表成绩,
请用SQL语句实现:
create table S(
sno int(10) primary key,-- 学号
sname varchar(20) not null,-- 姓名
sa int(3),–年龄
sd varchar(10)-- 所在系
)
create table C(
cno int(10) primary key,-- 课程号
cn varchar (20) unique , – 课程名
tname int(3) not null–任课老师
)
create table SC(
sno int(10),-- 学号
cno varchar(20), – 课程号
g int(3),–成绩
constraint fk_sc_s foreign key(sno) references s(sno)
constraint fk_sc_c foreign key(cno) references c(cno)
primary key (sno,cno)
)
将下列学生信息插入学生关系中:李丹,18岁,电信系,学号:20070206
INSERT into S(SNO,SNAME,SD) VALUES(20070206,‘李丹’,‘电信系’);
找出选修了课程为“112002”的学生学号和姓名
SELECT S.SNO,S.SNAME FROM S S,SC C where S.SNO=C.SNO AND C.CNO=‘112002’;
修改学号为“20070206”的学生所在的系为计算机
UPDATE S SET SD=‘计算机’ WHERE SNO=‘20070206’
查询选修了数据库系统原理这门课的学生的姓名和成绩
SELECT S.SNAME,C.G FROM S S,SC C where S.SNO=C.SNO;
JSP一共有9个内置对象:
request:负责得到客户端请求的信息,对应类型:javax.servlet.http.HttpServletRequest
response:负责向客户端发出响应,对应类型:javax.servlet.http.HttpServletResponse
session:负责保存同一客户端一次会话过程中的一些信息,对应类型:javax.servlet.http.httpsession
out:负责管理对客户端的输出,对应类型:javax.serlvet.jsp.jspwriter
application:表示整个应用环境的信息,对应类型:javax.servlet.servletcontext
config:表示ServletConfig,对应类型:javax.servlet.servletconfig
exception:表示页面中发生的异常,可以通过它获得页面异常信息,对应类型:java.lang.exception
pagecontext:表示这个JSP页面本身,对应类型:javax.servlet.jsp.pagecontext
page:表示当前JSP页面编译的servlet对象,数据类型:Object。
JSP:include (当页面被请求的时候引入一个文件)
JSP:forward (将请求转到另一个页面)
JSP:useBean (获得JavaBean的一个实例)
JSP:setProperty (设置JavaBean的属性)
JSP:getProperty (获得JavaBean的属性)
若在servlet配置中设置正整数或零,依次在服务器启动的时候就实例化,初始化init该servlet,再根据客户端请求到servlet时,执行service()方法,
当服务器容器关闭,该servlet执行destory()方法;
若在servlet配置中设置负整数或不配置,当客户端访问到该servlet时候,servlet实例化,初始化init(),再根据客户端请求到servlet时,执行service()方法,当服务器容器关闭,该servlet执行destory()方法
直接转发方式(forward): 客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。
重定向方式(redirect): 实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。
答:page、include、taglib
a、page指令:定义页面的一些属性,常用属性:
contentType=“text/html;charset=utf-8”; 向浏览器端输出数据的编码
pageEncoding=“utf-8”; JSP编译成java文件时所用的编码
session=“true” 是否自动创建session
b、include指令:引入一个静态的JSP页面
c、taglib指令:引入一个标签库
a、静态include:语法:<%@ include file=“文件名” %>,相当于复制,编辑时将对应的文件包含进来,当内容变化时,不会再一次对其编译,不易维护。
b、动态include:语法:
根据工厂模式实现的类可以根据提供的数据生成一组类中某一个类的实例,通常这一组类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作。首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法。然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
MVC的具体含义是:Model+View+Control,即模型+视图+控制 它们各自处理自己的任务: (1)模型Model:模型持有所有的数据、状态和程序逻辑。
(2)视图 View:用来呈现模型。视图通常直接从模型中取得它需要显示的状态与数据。对于相同的信息可以有多个不同的显示形式或视图。
(3)控制器Control:位于视图和模型中间,负责接受用户的输入,将输入进行解析并反馈给模型,通常一个视图具有一个控制器。
MVC模式将它们分离以提高系统的灵活性和复用性,MVC模式实现了模型和视图的分离,这带来了几个好处。
(1)一个模型提供不同的多个视图表现形式,也能够为一个模型创建新的视图而无须重写模型。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地刷新自己。
(2)模型可复用。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。
(3)提高开发效率。在开发界面显示部分时,你仅仅需要考虑的是如何布局一个好的用户界面;开发模型时,你仅仅要考虑的是业务逻辑和数据维护,这样能使开发者专注于某一方面的开发,提高开发效率。
(1)get是用来从服务器上获取数据,而post是用来向服务器传递数据;
(2)get将表单中数据按照variable=value的形式,添加到action所指向的URL后面,并且两者用"?“连接,变量之间用”&"连接;
而post是将表单中的数据放在form的数据体中,按照变量与值对应的方式,传递到action所指定的URL。
(3)get是不安全的,因为在传输过程中,数据是被放在请求的URL中;而post的所有操作对用户来说都是不可见的。
(4)get传输的数据量小,这主要应为受url长度限制;而post可以传输大量的数据,所有上传文件只能用post提交
(5)get限制form表单的数据集必须为ASCII字符;而post支持整个IS01 0646字符集。
(6)get是form表单的默认方法。
1. 服务器会把长时间没有活动的Session从服务器内存中清除,此时Session便失效。Tomcat中Session的默认失效时间为30分钟。
2. 调用Session的invalidate方法。
JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。JSP编译后是“类servlet”
JSP更擅长表现于页面显示,Servlet更负责于逻辑控制.
JSTL是Jsp Standard Tag Library的缩写。这是一组通用标签并将成为JSP 2.0的一部分。
其中core核心标签库包含out,foreach,if,set,choose,when,otherwise等标签。
a、page是代表一个页面相关的对象和属性。一个页面由一个编译好的java servlet类(可以带有include指令,但不可以带有include动作)表示。这既包括servlet又包括编译成servlet的jsp页面。
b、request是代表与web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个web组件(由于forware指令和include动作的关系)
c、session是代表与用于某个web客户机的一个用户体验相关的对象和属性。一个web回话也可以经常跨域多个客户机请求。
d、application是代表与整个web应用程序相关的对象和属性。这实质上是跨域整个web应用程序,包括多个页面、请求和回话的一个全局作用
使用session技术:(如注册示例)
a、在注册表单页面中生成一个随机值,将其保存到session中,同时将其保存为表单的隐藏域的值。
b、在处理注册的请求时,获取session中的值,获取请求参数的值,比较两者是否相同,如果相同说明不是重复提交,请求通过同时删除session中保存的值,如果不相同则是重复提交,不能通过。
a、config:配置文件存放路径
b、webapps:项目部署的目录
c、bin Tomcat运行需要的脚本与jar的目录
d、lib:运行项目时所需要的jar的目录
e、work:部署项目的缓存目录
f、temp:临时文件的存放目录
g、logs:记录日志的目录
轻量级是指它的创建和销毁不需要消耗太多的资源,意味着可以在程序中经常创建和销毁对象。
重量级意味着不能随意的创建和销毁它的实例,会占用太多的资源。
给处于其中的应用程序组件(JSP、Servlet)提供一个环境,是JSP、Servlet直接跟容器中的变量交互,不必关注其他系统问题。
主要有web服务器来实现。例如:tomcat、weblogic、JBoss、sphere等。该容器提供的接口严格遵守J2EE规范中的web application标准。
我们把遵守以上标准的web服务器叫做J2EE的web容器。
**概念:**对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中.
ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
Java典型的ORM中间件有:Hibernate,Mybatis等。
Mybatis把SQL语句从Java源程序中独立出来, 放在单独的XML文件中编写,给程序的维护带来了很大便利。
Mybatis封装了底层JDBC API的调用细节,并能自动将结果集转换成JavaBean对象, 大大简化了Java数据库编程的重复工作。
因为Mybatis可以灵活控制sql语句, 因此能够实现比hibernate等全自动orm框架更高的查询效率,能够完成复杂查询
mybatis的查询缓存分为一级缓存和二级缓存,
一级缓存: 是SqlSession级别的缓存,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域(HashMap)是互相不影响的。
如果SqlSession执行了DML操作(insert、update、delete),并执行commit()操作,mybatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存数据中存储的是最新的信息,避免出现脏读现象。
Mybatis默认开启一级缓存,不需要进行任何配置。
二级缓存: mapper级别的缓存,二级缓存是多个SqlSession共享的。
它同样是使用HashMap进行数据存储,相比一级缓存SqlSession,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
mybatis通过缓存机制减轻数据库压力,提高查询性能。
Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。
延迟加载的条件:resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
延迟加载的好处:
先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
延迟加载的实例:
如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
Mybatis的延迟加载功能默认是关闭的
需要在mybatis-config.xml文件中通过setting标签配置来开启延迟加载功能
开启延迟加载的属性:
lazyLoadingEnabled:全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。默认为false
aggressiveLazyLoading:当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。默认为true
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理 时 , 就 是 把 {}时,就是把 时,就是把{}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
流程说明:
(1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。
(2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
(3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。
(4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻。
(5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
(6)ViewResolver会根据逻辑View查找实际的View。
(7)DispaterServlet把返回的Model传给View。
(8)通过View返回给请求者(浏览器)
Spring MVC
Spring框架提供了构建Web应用程序的全功能MVC模块,即Spring MVC
Spring MVC框架,提供了一个DispatcherServlet,作为前端控制器来分派请求,同时,提供了灵活的配置处理程序映射、视图解析、语言环境和主题解析,并支持文件上传
Spring MVC特点
1、拥有强大的灵活性,非侵入性和可配置性
2、提供了一个前端控制器DispatcherServlet,开发者无需额外开发控制器对象
3、分工明确,包括控制器、验证器、命令对象、模型对象、处理程序映射视图解析器,每一个功能实现由一个专门的对象负责完成
4、可以自动绑定用户输入,并正确的转换数据类型
比如,Spring MVC能自动解析字符串,并将其设置为模型的int或者float类型的属性
5、使用一个名称/值的Map对象,实现更加灵活的模型数据传输
6、内置了常见的校验器,可以校验用户输入,如果校验不通过,则重定向会输入表单,输入校验是可选的,并且支持编程方式及声明方式
7、支持国际化,支持根据用户区域显示多国语言,并且国际化的配置非常简单
8、支持多种视图技术,常见的有JSP及其其他技术,包括Velocity和FreeMarker
9、提供了一个简单而强大的JSP标签库,支持数据绑定功能,使得编写JSP页面更加容易
为什么要使用Spring:
AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP后,公共服务(比如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。
IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反过来的JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straightconstruction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。
Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了Spring,只要用JavaBean属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文:Spring 上下文是一个配置文件,向 Spring框架提供上下文信息。Spring上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了Spring框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖EJB组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBCDAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。SpringDAO的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM的对象关系工具,其中包括JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和DAO异常层次结构。
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText 和 POI。
spring 框架的功能可以用在任何 J2EE服务器中,大多数功能也适用于不受管理的环境。Spring的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web或EJB)、独立应用程序、测试环境之间重用。
在Spring容器中为一个bean配置依赖注入有三种方式:
使用属性的setter方法注入
使用构造器注入
使用filed注入(用于注解方式)
**控制反转( Inversion of Control )**对软件而言,某一个接口具体实现的选择控制权从调用类中移除,转交给第三方容器决定。 (换而言之,即通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。)
DI(Dependency Injection) 依赖注入 让调用类对某一接口实现类的依赖关系由第三方容器注入,以移除调用类对某一接口的实现类的依赖。
AOP的全称是Aspect Oriented Programming,面向切面编程。是对OOP(Object Orient Programming)的一种补充,专门用于处理一些具有横切性质的服务。分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
这样就能****减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
日志功能 log4j
安全检查
事务管理
缓存
Spring AOP采用JDK动态代理机制和CGlib字节码生成技术实现。
**JDK动态代理:**基于接口的代理,一定是基于接口,会生成目标对象的接口类型的子对象。其核心的两个类是InvocationHandler和Proxy。
**CGLib代理:**基于类的代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强.
Spring既支持编程式事务管理(也称编码式事务),也支持声明式的事务管理
声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理
事务传播行为就是多个事务方法调用时,如何定义方法间事务的传播。Spring定义了7种传播行为:
(1)propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。
(2)propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
(3)propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
(4)propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
(5)propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
(6)propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
(7)propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
注解与XML配置的区别
注解:是一种分散式的元数据,与源代码紧绑定。
xml:是一种集中式的元数据,与源代码无绑定。
因此注解和XML的选择上可以从两个角度来看:分散还是集中,源代码绑定/无绑定。
通用配置还是走XML吧,比如事务配置,比如数据库连接池等等,即通用的配置集中化,而不是分散化, 比如数据源、事务管理
注解的好处: 数据绑定用注解,很少改变的用注解,类型安全,
还一种就是约定大于配置,但是在处理一些复杂的情况下,注解还是需要的
不管是约定大于配置、注解还是XML配置也好,没有哪个是最优的,在合适的场景选择合适的解决方案这才是重要的。就像设计模式一样:是对特定环境中重复出现的特定问题的一个经过前人验证了的解决方案。
(1)Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。
(2)Prototype:每次调用新建一个Bean实例。
前端控制器 DispatcherServlet
作用:接收请求、响应结果 相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度
处理器映射器HandlerMapping
作用:根据请求的URL来查找Handler
处理器适配器HandlerAdapter
视图解析器 ViewResolver
作用:进行视图的解析 根据视图逻辑名解析成真正的视图
视图View
作用:View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,所谓简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,所以 SpringBoot是一个服务于框架的框架,服务范围是简化配置文件。
(1)继承了Spring优秀的底层框架
SpringBoot是伴随着Spring 4.0而生的,boot是引导的意思,也就是它的作用其实就是在于帮助开发者快速的搭建Spring框架
(2)简化编码
Spring Boot 则会帮助开发着快速启动一个 web 容器,在 Spring Boot 中,我们只需要在 pom 文件中添加如下一个 starter-web 依赖即可
Spring Boot 这个 starter-web 已经包含了多个依赖,包括之前在 Spring 工程中需要导入的依赖
(3)简化配置
Spring Boot更多的是采用 Java Config 的方式,对 Spring 进行配置
public class TestService {
public String sayHello () {
return “Hello Spring Boot!”;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public TestService getTestService() {
return new TestService();
}
@Configuration表示该类是个配置类,@Bean表示该方法返回一个 Bean。这样就把TestService作为 Bean 让 Spring 去管理了,在其他地方,我们如果需要使用该 Bean,和原来一样,直接使用@Resource注解注入进来即可使用,非常方便。
部署配置方面,原来 Spring 有多个 xml 和 properties配置,在 Spring Boot 中只需要个 application.yml即可。
(4)简化部署
在使用 Spring 时,项目部署时需要我们在服务器上部署 tomcat,然后把项目打成 war 包扔到 tomcat里,在使用 Spring Boot 后,我们不需要在服务器上去部署 tomcat,因为 Spring Boot 内嵌了 tomcat,我们只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目。
另外,也降低对运行环境的基本要求,环境变量中有JDK即可
(5)简化监控
我们可以引入 spring-boot-start-actuator 依赖,直接使用 REST 方式来获取进程的运行期性能参数,从而达到监控的目的,比较方便。但是 Spring Boot 只是个微框架,没有提供相应的服务发现与注册的配套功能,没有外围监控集成方案,没有外围安全管理方案,所以在微服务架构中,还需要 Spring Cloud 来配合一起使用。
(6)从未来发展趋势看
微服务是未来发展的趋势,项目会从传统架构慢慢转向微服务架构,因为微服务可以使不同的团队专注于更小范围的工作职责、使用独立的技术、更安全更频繁地部署。而 继承了 Spring 的优良特性,与 Spring 一脉相承,而且 支持各种REST API 的实现方式。Spring Boot 也是官方大力推荐的技术,可以看出,Spring Boot 是未来发展的一个大趋势。
· 约定优于配置,降低搭建复杂度
· 大部分第三方配置库开箱即用
· 内置微型tomcat,无需再装tomcat进行部署项目,maven打包jar就可以直接运行项目
· 自动装配Spring
· 准生产的应用监控
· 无代码生产和xml配置
· SpringBoot不是借助与代码生成来实现的,而是通过条件注解来实现的,这是Spring4.x提供的新特性。
@Override 重写, 标识覆盖它的父类的方法
@Deprecated 已过期,表示方法是不被建议使用的
@Repository: 用于标注数据访问组件,即DAO组件
@Service: 用于标注业务层组件
@Controller: 用于标注控制层组件
@Component: 把该中立的类交给spring管理
@Autowired: 自动装配,将bean容器里的值自动注入到bean
@Transactional: 声明这service所有方法都需要事务管理。每一个业务方法开始时都会打开一个事务
先来看应用,再理解:
在Restful之前的操作:
http://127.0.0.1/user/query/1 GET 根据用户id查询用户数据
http://127.0.0.1/user/save POST 新增用户
http://127.0.0.1/user/update POST 修改用户信息
http://127.0.0.1/user/delete GET/POST 删除用户信息
RESTful用法:
http://127.0.0.1/user/1 GET 根据用户id查询用户数据
http://127.0.0.1/user POST 新增用户
http://127.0.0.1/user PUT 修改用户信息
http://127.0.0.1/user DELETE 删除用户信息
GET:获取资源的方法;
PUT:更新资源的方法;
POST:创建资源的方法;
DELETE:删除资源的方法;(比较常见的增删改查)
返回结果:返回的结果包括请求的状态码和得到的资源,返回的结果用json或xml格式表示,json比较常见,因为json相对xml更加轻量,传输过程更小更快,加上解析支持更广
SpringMVC实现restful服务:
SpringMVC原生态的支持了REST风格的架构设计
所涉及到的注解:
–@RequestMapping
—@PathVariable
—@ResponseBody
@RequestMapping(“user”)
@Controller
public class RestUserController {
@RequestMapping(value = “{id}”, method = RequestMethod.GET)
@ResponseBody
public User queryUserById(@PathVariable(“id”) Long id) {}
@RequestMapping(method = RequestMethod.POST)
public Result saveUser(User user) {}
@RequestMapping(method = RequestMethod.PUT)
public Result updateUser(User user) {}
@RequestMapping(method = RequestMethod.DELETE)
public Result deleteUser(@RequestParam(value = “id”, defaultValue = “0”) Long id) {}
}
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义.如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。
(1)资源(Resources)
REST的名称"表现层状态转化"中,省略了主语。“表现层"其实指的是"资源”(Resources)的"表现层"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源标识符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
附:
URI:Uniform Resource Identifier,统一资源标识符
URL:Uniform Resource Location统一资源定位符
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
(2)表现层(Representation)
“资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层”(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
(3)状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
1、硬件升级
2、服务器集群、负载均衡、分布式
3、CDN
4、页面静态化
5、缓存技术(Memcache、Redis)
以上为架构层面
以下为网站本地项目层面
6、数据库优化
1、数据库分表技术
2、数据库读写分离
3、表建立相应的索引
7、禁止盗链
8、控制大文件的上传下载
validate:验证项目是正确的,所有必要的信息都是可用的
compile:编译项目的源代码
test:使用适当的单元测试框架测试编译后的源代码。这些测试不应要求将代码打包或部署
package:使用已编译的代码,并将其打包成可分布格式,例如JAR。
verify:对集成测试的结果进行任何检查,以确保满足质量标准
install:将包安装到本地存储库中,以便在本地其他项目中使用该包
deploy:在构建环境中完成,将最终的包复制到远程存储库中,以便与其他开发人员和项目共享。
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
XML: 扩展标记语言 (Extensible Markup Language, XML)
JSON(JavaScript Object Notation)一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。
XML的优缺点
<1>.XML的优点
A.格式统一,符合标准;
B.容易与其他系统进行远程交互,数据共享比较方便。
<2>.XML的缺点
A.XML文件庞大,文件格式复杂,传输占带宽;
B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
D.服务器端和客户端解析XML花费较多的资源和时间。
JSON的优点:
A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
B.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;
C.支持多种语言,支持服务器端语言,便于服务器端的解析;
D.在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;
E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
<2>.JSON的缺点
JSON格式目前在Web Service中推广还属于初级阶段。