咱们知道计算机只认识1,0两种电平的信号,全部信息或者计算指令最终都编码成16进制的机器码,这些机器码做为程序保存于计算机的内存中,由CPU去单个取指令执行直到程序执行完毕。然而计算机能认识的这些机器码确实不是人类善于处理的,所以人们发明了汇编语言,随后使用汇编器(assembler)翻译成为机器码;再随后贝尔实验室发明了C语言,这个就是人类可以理解并创造的高级程序了。一样地,要在CPU上运行,咱们必须翻译成机器码,这个由编译器来完成。咱们来看下面一句程序:javascript
printf(1+2);
在x86 intel机器上翻译成机器码就是 101000101010 ,intel x86处理器运行这个代码,其中也包含了系统调用的机器码,在这里有两个最关键的东西:1.intel处理器,2.操做系统。这二者的组合咱们就称为platform.显然对应不一样的处理器和不一样的操做系统会有不一样的排列组合。对于咱们应用开发者来讲,咱们固然但愿咱们的应用程序能够很方便地在不一样平台上运行。那咱们来看看,有哪些方案:php
1. 针对不一样的处理器汇编问题,咱们须要购买host在windows下的针对不一样target cpu的汇编器负责转换出可以target到不一样cpu的机器码;这个过程叫作交叉编译。而且不一样的cpu输出不一样的机器码文件,不能混用css
2. 针对不一样的操做系统,咱们printf作的系统调用也会有所不一样html
可是咱们要知道的是交叉编译而且针对不一样OS特性作不一样的适配,自己就是很是复杂昂贵的,不是每一个高级语言开发工程师都能胜任的。这就有了java出现的机会,咱们来看看java平台是如何解决这个问题的。前端
java平台中有一个很是重要的模块: Java Virtual Machine,全部预编译过的bytecode就在这个虚拟机上运行。详细过程以下:java
1. printf(1+2)这个代码用java就是System.out.println(1+2),该文本保存为.java文件;python
2.使用java编译器将这个.java文件转换成称为bytecode的中间代码,输出为.class文件;mysql
3.这个.class内容自己并不和任何特定平台相关,也就是说任何平台自己是没法直接运行的;linux
4.这个虚拟机驻留在操做系统的内存中,当虚拟机被喂入这些bytecode时,jvm会识别到咱们正在工做的具体平台而且将bytecode最终转换为native machine codeandroid
这样你的代码只要编译一次,就能在全部平台上运行!!
所以, java既是一个编程语言,更是一个平台。
其余的编程语言,好比C语言,编译器产生targeted到特定平台的机器码,好比wintel,好比linux+intel等,而java compiler只会将源程序编译target到Java Virtual Machine. Bytecode是host system和java source的桥梁
https://www.guru99.com/java-virtual-machine-jvm.html
Maven是一个java的构建工具,相似于C语言的make,同时Maven也是一个依赖管理的工具。
In short, Archetype is a Maven project templating toolkit. An archetype is defined as an original pattern or model from which all other things of the same kind are made. The name fits as we are trying to provide a system that provides a consistent means of generating Maven projects. Archetype will help authors create Maven project templates for users, and provides users with the means to generate parameterized versions of those project templates.
maven提供了不少工程模版,当建立一个新项目时,就可使用这些模版,自动建立配置好对应的环境
IDE对比:
Eclipse, IntelliJ Idea, myEclips等,我比较喜欢Idea,特别地,java for android新的正式工具也是基于idea设计的。写代码超级爽.使用Idea能够开启一个j2ee hello world程序来学习
java主要分为3大平台:
java SE (J2SE)= standard edition:这是核心的java编程平台,包含了java.lang,java.io,java.math,java.net,java.util等通用的库和API。主要用于桌面应用开发
java ee (J2EE)= enterprise edition: 在SE基础上,增长了用于部署高容错,分布式,多层级的java软件的库,这些基本上都以模块化的组件模式运行在application server上。也就是说,若是你的应用会造成巨大规模,分布式的系统,那么你应该考虑JAVA EE。它还提供包括数据库访问(JDBC,JPA),远程函数调用RMI,消息(JMI),web services, XML解析,而且定义了企业级的JavaBeans, servlets, portlets, Java Server Pages等标准API。主要用于web(网络)应用开发
java me(J2ME) = mico edition.用于开发mobile device上的应用,嵌入于相似机顶盒的设备中。主要用于手机应用
JRE = Java Runtime Environment = JVM + API(Lib)
JRE运行程序时的三项主要功能:由class loader来加载代码,由bytecode verifier来校验代码,由runtime interpreter来执行代码
一句话:由虚拟机来装载编译好的应用程序而且调用相应的指令具体地执行。虚拟机能够理解为在实际的wintel, linux/intel平台上虚拟出一个新的机器,有他本身的指令系统,操做系统API接口,对下会匹配到不一样的平台,对上展现的接口是相同的,所以具备跨平台的特征。
JDK = JRE+ Tools = JVM + API + Tools
JDK提供如下主要的开发工具:
javap反汇编后造成的jvm字节码指令和源程序的对应关系:
public class Main { public static void main(String[] args) { System.out.println("hello, world"); } } J:\eclipse-workspace\TType>javap -c out\production\TType\Main.class Compiled from "Main.java" public class Main { public Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello, world 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
二者的运行环境不一样,application是独立的程序,须要执行器(调用虚拟机)来运行;而applet则是嵌在HTML页面中的非独立程序,由专门的appletViewer来运行,或者由web浏览器调用JAVA虚拟机来运行.applet是java最先提出的,是对网页web技术发展的重大改进,今后网页变得丰富,可交互了。要在网页中运行applet必须在系统中有JRE安装,而且要在浏览器中使能java,咱们须要将.class以及.html文件放到www服务器上,而后用浏览器访问。从java8开始, applet的运行就收到更加严格的限制。
applet替代方案有flash, silverlight等。甚至如今随着javascript富体验方案以及HTML5提供的丰富功能引入,applet已经慢慢退出历史舞台.
.jar是一系列java的类字节文件打包生成的压缩文件,jar包伴随一个很是重要的是清单文件(mainifest),指出这个jar包的入口类是哪个
要使用jar包通常通过:编译->打包->运行三个阶段
javac A.java
jar cvfm A.jar manifest.mf A.class
java -jar A.jar
application输入输出能够是文本界面,也能够图形界面,而applet则只能是图形界面
文本界面的输入输出: java.util.Scanner类
图形界面输入输出:初始化一个frame,在这个frame中add一些控件,控件中添加事件处理
基本数据类型:byte,short,int,long,float,double,char,boolean存在栈里面
引用数据类型:class,interface,数组:数据自己存在堆里面,可是指针存在栈里面
看下面的代码, 因为常量是存在方法区中的常量池中,值相等的常量就是同一个常量,而字符串"aaa"本质上就是一个“常量”.可是若是经过new String方式建立的字符串则存在于系统堆中,局部变量仅仅是指向了堆中分配的内存而已。因为字符串本质上是一个常量,而咱们常常须要作字符串拼接赋值,每一次这类操做都会产生一个字符串副本,效率低下。为解决这个问题,须要使用StringBuffer,也就是能够变动的string。
public static void main(String[] args) { String s1= "aaa"; String s2="aaa"; System.out.println(s1==s2); // true String s3 = new String("aaa"); String s4 = new String("aaa"); System.out.println(s3==s4); // false System.out.println(s3.equals(s4)); // true System.out.println(s3==s2);// false String s5 = "aaa"; String s6 = s5; s6 = s6 + "bbb"; // 字符串是不可变的,每次修改本质上都是建立了一个副本 System.out.println(s5==s6); // false StringBuffer s7 = new StringBuffer("aaa"); StringBuffer s8 = s7; s7.append("bbb"); System.out.println(s7==s8); // true }
package实际上为了解决名字空间,名字冲突,相似php的命名空间,须要和对应存储路径相对应
package pkg1[.pkg2]
根目录由classpath环境变量来肯定,若是没有package语句,则是default package.
java jdk实际上提供了不少包,好比: java.applet,java.awt,java.awt.image,java.net,java.util等
为了使用java中提供的类,须要用import语句来导入所须要的类。
import java.util.Date
导入类后就能够直接使用Date而不用加package的前缀了。
http://www.cnblogs.com/moveofgod/p/3809653.html
第三方的Package通常都以jar包形式来发布。咱们知道jar包其实是包含了一堆类字节码的压缩包。咱们要在项目中使用,首先得引入jar包,并将对应jar包加到对应的build path,其本质是classpath加入jar包,这样在java代码中import 第三方jar包中的类时,jvm就能找到对应的类字节码并加载运行。
https://blog.csdn.net/zhenyusoso/article/details/6174834
javac -d . pk\*.java
java pk.TestPkg /*pk是package, TestPkg是包含main的class*/
须要注意的是,默认若是没有任何修饰符,则成员在同一个包中能够访问.首先要看是否类为public,也就是说是否能够在其余package中可以访问该类,只有有权访问类,再谈可否访问该类的方法。
public class PubClass{ defaultFunction(){} // 默承认以被包内访问 privateFunction(){} // 只能在本类中访问 protectedFunction(){} // 在子类中也可以访问 publicFunction(){} // 任何地方都可以访问 }
对于私有的成员,咱们通常经过提供setter/getter函数提供读和/或写,好处是可以作合法检查,作量刚的变换等。。若是不提供setAttribute方法则该属性就是只读的
很是常见的,好比System.in和System.out其中in和out就是在System这个类中定义的static变量,所以任什么时候候均可以访问。
static如修饰方法,则该方法属于类,不被任何类所专有,在调用该static方法时,无须事先实例化一个对象
一样,若是没有static关键字则方法会属于特定的对象。因为static方法属于类,而不属于对象,所以static方法不能操做属于类实例的成员变量!static方法不能使用this或super
final类表示不可被继承,final方法表示不可被子类覆盖overwrite的方法,final字段表示一旦初始化就不可改变!
static final修饰某个字段时,该字段为常量,好比Math.PI, integer.MAX_VALUE都是static final类型的成员变量
abstract方法必须在子类中实现, abstract类不能被初始化
接口就是某种特殊的约定,接口能够被多个类来实现,软件工程趋势于:面向接口的编程,而不是面向实现。
经过接口能够实现不相关的类的相同的行为,而无需考虑这些类之间的层次关系。某种意义上实现了多重继承。
接口和类的继承层次无关,同一个接口能够被不一样的类来实现。
好比上面这个图中: Flyable这个接口定义了takeoff,fly,land三个接口方法,该接口被Airplane, Bird, Superman三个类来implement了,可是这三个类分别继承于Vehicle, Animal这两个抽象类(abstract class)
JAVA 8中实现了接口函数的默认实现,这样有点像似继承了,不用每一个申明implements这个接口的类中都要实现每个函数。
字段变量为对象的一部分,所以存在于对象中,也就是堆中;
局部变量为成员函数内声明的变量,存在于栈中
生命周期不一样:字段变量随对象建立后一直存在直到对象销毁,而局部变量只有在函数被调用时存在,调用结束则释放内存;
字段变量自动赋初值为0, 局部变量则不会自动初始化,必须显式地初始化。
总的来讲,调用对象方法时,java是值传递,即:将表达式的值复制给形式参数。对于引用型变量,传递的值是引用值,不会复制对象实体自己
多态是指一个程序中相同的名字表示不一样的含义。java的多态有两种情形:
public class Main { static void callDraw(Shape s){ s.draw(); } public static void main(String[] args) { Circle c = new Circle(); Triangle t = new Triangle(); Line l = new Line(); callDraw(c); callDraw(t); callDraw(l); } } class Shape{ void draw(){ System.out.println("shape drawing");} } class Circle extends Shape{ void draw(){ System.out.println("Circle drawing");} } class Triangle extends Shape{ void draw(){ System.out.println("triangle drawing");} } class Line extends Shape{ void draw(){ System.out.println("line drawing");} }
上面这个例子中到底调用的是哪一个draw则在运行时决定。
java中,普通的方法就是虚方法,可是如下几种情形不是虚方法调用:
static/private/final
请注意下面的例子中,因为Shape中的draw为static也就是说属于类的,就不会触发虚方法调用,而输出3个相同的shape drawing。这时的调用依赖于申明的类,这里就是Shape类,和传入的是circle,triangle等无关了
public class Main { static void callDraw(Shape s){ s.draw(); } public static void main(String[] args) { Circle c = new Circle(); Triangle t = new Triangle(); Line l = new Line(); callDraw(c); callDraw(t); callDraw(l); } } class Shape{ static void draw(){ System.out.println("shape drawing");} } class Circle extends Shape{ static void draw(){System.out.println("Circle drawing");} } class Triangle extends Shape{ static void draw(){System.out.println("triangle drawing");} } class Line extends Shape{ static void draw(){System.out.println("line drawing");} } // shape drawing // shape drawing // shape drawing
在建立一个对象时,若是类中没有构造函数,则系统会自动调用super,这时必定要注意父类中的构造函数必须是无参数的函数,不然就会出错。java编译器的原则是必须令全部父类的构造方法都能获得调用
所以,若是不显式地调用super,则必须保证其父类中的构造函数为无参数,不然编译出错
public class Main { public static void main(String[] args) { InitialTest2 init2 = new InitialTest2(2); } } class InitialTest{ static int x=0; static { x++; System.out.println("static..."+x); } } class InitialTest2 extends InitialTest{ InitialTest2(int a){ this.a = a; System.out.println("consturction2 : this.a="+a); } int a; { System.out.println("IntialTest2 before instance created..."+this.a); } static { x++; System.out.println("static2 init..."+x); } } /* 输出结果: static...1 static2 init...2 IntialTest2 before instance created...0 consturction2 : this.a=2 *
因为经过super调用在construct中可能会在调用虚方法时绕回到子类中访问未初始化的数据,所以尽可能不要在构造函数中调用方法,若是必须调用的话就调用final方法
System.gc()调用仅仅建议启动系统的垃圾回收,可是并不能真正作到垃圾的回收。
也能够在子类的finalize()重载实现中去释放资源,相似c++的析构函数。
对于实现了java.lang.AutoCloseable的对象,咱们可使用try范式在代码执行完毕后自动关闭资源:
try(Scanner scanner = new Scanner(...)) { ... }
匿名类没有类名,在定义类的同时就生成该对象的一个实例,一次性使用
至关于匿名函数
(参数)->结果
的形式,相似于javascript的匿名函数
@override是java的伪代码,表示在这里要重写下面的方法,固然也能够没有。写了这个伪代码的好处:
一、能够当注释用,方便阅读;
二、编译器能够给你验证@Override下面的方法名是不是你父类中全部的,若是没有则报错。例如,你若是没写@Override,而你下面的方法名又写错了,这时你的编译器是能够编译经过的,由于编译器觉得这个方法是你的子类中本身增长的方法。
注意重载和重写的区别,重载是方法的参数个数或者参数类型不一样,致使是不一样的方法。而重写是子类对父类相同方法的覆盖重写
POJO = Plain Old Java Object字面意思是普通java对象。其内在含义为没有继承任何类的普通类实例对象。
一个pojo类(plain old java object)对象中,若是其字段都有对应的getter和setter,而且有一个无参的构造函数,这类对象咱们称之为JavaBean,JavaBean每每由容器来建立,好比tomcat建立javabean,所以须要保证有一个无参构造函数。更多用于数据的临时中转
war文件是一个适用于tomcat webapp目录下部署的web项目包文件。一般使用一下命令打包和查看,一般包含一堆的jar包文件
jar -cvf blog.war * // 打包 jar -tf blog.war //查看
有的时候,具体使用什么class的参数,只有在使用时才能肯定,那么比较好的方案就是使用范型。好比要设计一个Point类,其成员变量可能可使用整形,也可使用浮点数,那么就可使用:
public class Main { public static void main(String[] args) { Point<String> p1 = new Point<String>(); // 在调用处指定对应类型 p1.x = "20"; p1.y = "24"; System.out.println(p1.x); } } class Point<T>{ T x; T y; }
java程序运行时出错的话要么本身try{}catch主动处理,要么经过throws调用交由jvm自行处理。java程序也能够主动抛出异常,层层向上。
throw在方法体内,由java本身主动抛出异常,而throws则在方法后面紧跟本方法可能抛出的异常,而在别人调用这个异常时,就必须实现catch相应的异常,或者再次主动抛出异常。
类加载器负责将.class字节码文件加载到内存中,并为之生成对应的class对象。以便后续使用。
java反射机制是在运行状态中,对于任何一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取对象的信息以及动态调用对象方法的功能称为java语言的反射机制。
要使用反射,就必须得到字节码文件
public class Main { public static void main(String[] args) throws ClassNotFoundException { Class pclass1 = Class.forName("Point"); Class pclass2 = Point.class; Point pobj = new Point(); Class pclass3 = pobj.getClass(); System.out.println(pclass1==pclass2); // true System.out.println(pclass2 == pclass3); // true } }
以上说明几种方式获取class字节码都是相同的拷贝,其中若是使用idea则随时可能要使用alt+enter快捷键增长一个local变量来引用返回值,及快捷增长exception处理。
public class Main { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { Class pClass = Class.forName("Point"); Point pObj = (Point)pClass.newInstance(); Field privateVarField = pClass.getDeclaredField("privateVar"); privateVarField.setAccessible(true); privateVarField.set(pObj,205); System.out.println(pObj.getPrivateVar()); // 私有变量已被修改成205 } }
class Point{ private int privateVar = 1; int x; int y = 2; private int privateGetX(){ return x; } public int setX(int a){ x = a; return 0; } public int publicGetY() { return y; } } public class Main { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { Class pClass = Class.forName("Point"); Point pObj = (Point)pClass.newInstance(); pObj.setX(5); Method publicGetY = pClass.getMethod("publicGetY"); System.out.println(publicGetY.invoke(pObj)); // 2 Method privateGetX = pClass.getDeclaredMethod("privateGetX"); // 暴力破解private Method,使得能够经过反射来调用 privateGetX.setAccessible(true); System.out.println(privateGetX.invoke(pObj)); // 5 } }
在java中,一个类内部又能够定义内部类,内部类有如下几种存在形式
1.成员内部类
class OutClass{ private int num = 3; class Inner { // 成员内部类 int num = 4; void show(){ int num = 5; System.out.println("num" + num); // num:5 System.out.println("num" + this.num); // num:4 本内部类的成员 System.out.println("num" + OutClass.this.num); // num:3 外部类的成员访问 } } public static void main(String[] args){ OutClass.Inner inner = new OutClass().new Inner(); // 因为有内部成员类,所以有了new方法,须先new一个外部类实例,再new内部类 inner.show(); } }
2.静态内部类
class OutClass{ private static int num = 3; static class Inner { // 成员内部类 int num = 4; void show(){ int num = 5; System.out.println("num" + num); // num:5 System.out.println("num" + this.num); // num:4 本内部类的成员 System.out.println("num" + OutClass.num); // num:3只能访问外部类静态成员 } static void showstatic(){ int num = 5; System.out.println("num" + num); // num:5 System.out.println("num" + OutClass.num); // num:3 外部类的成员访问 } } public static void main(String[] args){ OutClass.Inner inner = new OutClass.Inner(); inner.show(); OutClass.Inner.showstatic(); } }
3.匿名内部类
匿名内部类每每用于从接口定义一个类,仅仅一次使用,没有必要给他一个命名的情形
4.局部内部类
tomcat做为server run time environment,以web容器的方式提供服务,包括connector(http,https,ajp及其余请求方式)的接入引擎,服务引擎,Host主机服务及host下面的context project等部分组成。
web层负责http请求的接收和响应;web层适用于MVC设计模式,controller实际上就是servlet,view则是JSP,model对于C操做,由input参数建立对应的model并经由service调用DAO持久化,对于R操做,则反过来由DAO层获取到数据呈现到response中.
service层负责核心的业务逻辑;
Dao层则惟一负责和数据库CRUD操做并以model方式返回数据
java线程在调用start方法后将进入就绪状态队列,该队列为一个先入先出的FIFO队列,CPU会根据必定的调度算法从该队列中取出一个线程分配时间片进入运行状态。
在运行过程当中,若是时间片到时则会被抢占进入就绪态,等待下一次调度;若是运行中须要等待某一资源,则阻塞本身进入等待态。线程运行完毕则销毁态。
class MyThread extends Thread { @Override public void run() { System.out.println("当前线程名称为:"+ Thread.currentThread().getName()); } } public class ThreadDemo{ public static void main(String[] args){ MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); // 分别打印 当前线程名称为:Thread-0,注意顺序多是随机的哦 myThread2.start(); // 分别打印 当前线程名称为:Thread-1 } }
servlet是运行在服务端的java程序段,遵循sun公司提供的一套规范接口。该接口中最重要的是service方法,还有init,destroy等接口。主要用来负责处理客户端的请求并响应给浏览器以动态资源。servlet的实质就是java代码,经过java提供的API来动态向客户端输出内容。
须要注意的是,在java web以及junit开发中,咱们不须要再写main函数,由于该main函数在javaweb开发时是由tomcat的bootstrap类来提供的,是一个无限循环,永远不会撤销。而在junit中则在junit的框架代码中。咱们的servlet程序由tomcat的main函数在接收到http请求时经过反射机制来具体调用servlet的字节代码,并最终返回响应。servlet是链接web前端和后端逻辑的桥梁。servlet是singleton,所以在servlet中不能保存用户相关的数据(至关因而全局互斥数据,会致使线程冲突),不然会致使混乱,注意线程安全
能够经过手工在web.xml中定义,或者经过注解@WebServlet("/myservnet")的方式来指示编译器完成映射。
servlet配置过程,相似于PHP Laravel的路由配置过程,主要要指定对应的url-pattern,以及对应的servlet类。配置时也存在优先级及覆盖的问题。咱们能够在站点级全局web.xml中配置公共路由,也能够在项目级别建立web.xml实现项目级别的路由。
当path后面写的是静态资源名称,好比index.html,tomcat也会去找url-pattern有没有能够匹配的内容,若是有,就加载对应servnet,若是没有就找到配置中的缺省url-pattern.
若是当前project没有缺省url-pattern,则找到站点级别的web.xml的默认匹配的url-pattern,通常在default servlet name项中
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
JSP自己实质上是一段未来被编译为字节码的java程序,也是一个servlet,相似于PHP的模版引擎(xxx.blade.php),最终编译出来的字节码(php native源码)完成的工做实际上就是拼接java数据到对应前端html中.
当tomcat收到一个http请求时,路由到servlet,对应的servlet根据业务逻辑可能须要forward到其余servlet(这是内部转移)或者直接返回一个重定向让浏览器作redirect操做,最终才能完成业务。
若是须要servlet共享数据给jsp,则须要使用forward转发,转发只能转到内部的资源。
每每经过request对象setAttribute增长一个数据,而后forward给jsp来显示数据
request.setAttribute('productList',proList);
RequestDispatcher requestDispatcher = request.getRequestDispatcher('/product/query.jsp');
requestDispatcher.forward(request,response);
JSTL/EL是用于简化jsp编写而定义的java规范,JSTL(tld)定义了一些描述性的标签,EL则定义了一种以${ java代码 }的方式便于在jsp中执行java代码获取数据,而且使用相似php的blade模版来表达展现数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>查询商品</title> </head> <body> <c:forEach items="${requestScope.prodList}" var="prod"> <tr> <td>${prod.id}</td> <td>${prod.name}</td> <td>${prod.price}</td> </tr> </c:forEach> </body> </html>
须要注意的是jstl标签里面访问的都是pojo的get方法,由于相似于name, price等字段都是私有的,因此不可能经过obj.propery来访问到,只能经过getXXX的方式来获取,这也是为何咱们须要JavaBean的缘由。咱们尽可能不要在jsp中使用<%= pageContext.request.contextPath %>,这样的方式来写java代码,而尽量要使用EL表达式方式
虽然在上面的演示中,咱们经过request,session,application等域对象能够在页面处理过程当中交换数据,可是这类方法更多限定于在jsp页面中访问相关数据,对于若是想在action中访问相关数据,则可使用struts2框架的值域。action一旦建立,就会生成一个valueStack,他就是一个存放数据的容器。struts2中会将全部的域对象(request,response,application,session)也都存放在了valueStack中,所以最好的方式,我们都统一称为valueStack方式来处理数据。
在web页面中,有一些css,js等前端资源的引用其实是公共的,几乎全部的页面都须要。而且有的时候咱们也须要在全部jsp文件中均可能须要引用相似工程基地址的变量,这时一个比较好的办法就是使用相似php的partial.blade.php文件,将这些东西抽取出来,在全部jsp文件中经过@include来引用。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <c:set var="projectbaseuri" value ="${pageContext.request.contextPath}"/>
所谓本地方法区是指非Java语言编写的C或者C++程序须要的栈,而程序计数器则属于线程专有,保存着每一个线程运行时场景的堆栈,PC计数器等,能够用于线程的切换
jdbc是java定义的一套java访问数据库的接口,数据库的vendor能够根据该接口规范来实现数据库访问驱动,好比mysql的驱动为Mysql-JDBC-impl.jar,Oracle-JDBC-impl.jar, SQL servier-JDBC-impl.jar.
java程序遵循JDBC规范经过对应的驱动来对实际数据库的访问。
经过上面的JDBC接口,虽然JAVA程序能够直接访问到数据库,可是每次数据库的访问仍是比较复杂的过程,好比创建链接,构建SQL,执行SQL,关闭sql statement,关闭jdbc链接。为了责任单一,通常须要抽象出来一层DAO组件,上层程序调用该组件的方法实现数据库操做。
如上面所描述,每次数据库的操做都须要创建数据库的链接,数据库操做完毕后将链接关闭,而socket创建和关闭是很消耗资源而且缓慢的过程,咱们有必要事先建立好这些connection,而数据库访问时,临时从这些connection中取出一个,数据操做完毕后并不真正释放链接,而是将链接对象返回到链接池,供后续应用使用。
相似于PHP laravel的middleware,咱们可使用filter来对某些servlet进行保护和受权。
咱们能够监听servletContext, HttpSession, ServletRequest对象的建立属性更改等事件。
须要注意的是针对监听的对象不一样,监听器的做用范围也是不一样的,好比针对监听servletContext,则是全局性质的,监听Session的,则只针对单个访问过程周期有效(只要浏览器没有关闭,session就存在),而监听ServletRequest的,则只针对单次请求有效
servlet < filter < listener优先级排序
因为listener优先级最高,最早执行,所以每每把整个项目的初始化数据加载工做放在这里执行
https://blog.csdn.net/sunxianghuang/article/details/52107376
https://blog.csdn.net/xiaojie119120/article/details/73274759
application:tomcat全局惟一
session:单用户惟一
request/response:单个pv惟一
JavaWeb的四大做用域为:PageContext,ServletRequest,HttpSession,ServletContext;
PageContext域:做用范围是整个JSP页面,是四大做用域中最小的一个;生命周期是当对JSP的请求时开始,当响应结束时销毁。
ServletRequest域:做用范围是整个请求链(请求转发也存在);生命周期是在service方法调用前由服务器建立,传入service方法。整个请求结束,request生命结束.
HttpSession域:做用范围是一次会话。生命周期是在第一次调用request.getSession()方法时,服务器会检查是否已经有对应的session,若是没有就在内存中建立一个session并返回。当一段时间内session没有被使用(默认为30分钟),则服务器会销毁该session。若是服务器非正常关闭(强行关闭),没有到期的session也会跟着销毁。若是调用session提供的invalidate() ,能够当即销毁session。
注意:服务器正常关闭,再启动,Session对象会进行钝化和活化操做。同时若是服务器钝化的时间在session 默认销毁时间以内,则活化后session仍是存在的。不然Session不存在。 若是JavaBean 数据在session钝化时,没有实现Serializable 则当Session活化时,会消失。
ServletContext域:做用范围是整个Web应用。当Web应用被加载进容器时建立表明整个web应用的ServletContext对象,当服务器关闭或Web应用被移除时,ServletContext对象跟着销毁。
做用域从小到大为:PageContext(jsp页面),ServletRequest(一次请求),HttpSession(一次会话),ServletContext(整个web应用)。
相似于python,c, java中也存在对对象持久化的需求.比较典型的例子是tomcat在关闭前会将session内存数据序列化存放到硬盘,而从新启动tomcat则反序列化读取恢复到内存。
在tomcat的conf/loggings.properties
文件或者该应用的WEB-INF/classes
目录中新建一个loggings.properties
文件,再加上如下两句:
org.apache.catalina.core.ContainerBase.[Catalina].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].handlers = java.util.logging.ConsoleHandler
https://blog.csdn.net/Q_AN1314/article/details/52832460
https://blog.csdn.net/zhengzhaoyang122/article/details/80142955
面向服务架构,和传统的单机单进程搞定一切业务需求不同,他更强调模块化,层次化,更加容错,系统容易维护,可是也会带来没必要要的复杂性。模块之间经过RPC或者REST API调用来通讯
在正常的开发过程当中,咱们写代码,build,从新部署,使用浏览器检查结果。每每build和从新部署是很是耗时也是频繁发生的,JRebel就是解决这个痛点的,相似于Nodejs、webpack中的HRM模块,在编写前端组件代码时,无需刷新浏览器,代码直接编译并灌入浏览器,这样的开发体验是很是高效完美的。
https://zeroturnaround.com/software/jrebel/pricing/
struts实际上就是web层的MVC框架,经过一个前端过滤控制器,截取全部的request,分发到对应的action,在action中能够经过result结果页返回对应的web页面(result实际上就是至关于laravel中的view)
spring是一个开放源码的设计层面框架,他将面向接口编程思想贯穿始终,是一个分层的JavaSE/JavaEE full-stack一站式轻量级开源框架,他主要为了解决业务逻辑层和其余各层的松耦合关系。这个更加相似于laravel
经过工厂实现接口与实现的分离。当需求变化时,好比一个mysql须要变动为oracle的数据库系统,则只须要配置bean对应的class类名,因为对象建立都是由spring提供的工厂来提供的,而工厂根据新的配置就能经过反射机制建立新的类对象了。
IOC本质上就是控制反转,由spring来给咱们建立类实例,而对应的类由咱们在xml中配置指定,方便解耦。
1. xml配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 这里bean id就是你要实例化类的alias,而其实现则由class来决定,届时由IOC自动调用这里定义的实现类 --> <bean id="userDao" class="cn.kidsit.project1.UserDaoOracleImpl">
<property name="userName" value="zzh"></property>
</bean> </beans>
2. 接口类及实现类:
package cn.kidsit.project1; public interface UserDao { public void save(); public void delete(); } public class UserDapMysqImpl implements UserDao { @Override public void save() { System.out.println("mysql save"); } @Override public void delete() { System.out.println("mysql delete"); } } public class UserDaoOracleImpl implements UserDao {
public String userName;
public void setUserName(String userName) {
this.userName = userName;
}
@Override public void save() { System.out.println("oracle save"); } @Override public void delete() { System.out.println("oracle delete"); } }
3.单元测试代码:
package cn.kidsit.project1; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserTest { @Test public void test(){ // 1.加载配置文件 ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2.根据id建立获取对象 UserDao userDao = (UserDao) appContext.getBean("userDao"); userDao.save(); // 这时将建立的是oracle的实现类 userDao.delete();
UserDaoOracleImpl userDIDao = (UserDaoOracleImpl) appContext.getBean("userDao");
System.out.println(userDIDao.userName); // 这里就有了初始化的值,因为依赖注入的功劳
} }
将spring管理的类中依赖的属性赋值时,经过配置文件来指定spring建立对象的过程就是依赖注入
在上面的xml配置代码中,默认建立的对象都是单例模式,也就是只建立了一个,之后都是使用相同的对象。可是不少时候咱们必须指定多例模式,好比action对于每次访问都是不一样的。
singleton:默认的scope配置,单例模式;
prototype:多例模式
request:应用在web项目中,Spring建立这个类以后,将这个类对象存入到request范围中;
session:应用在web项目中,Spring建立这个类以后,将这个类对象存入到session范围内;
globalsession:应用在web项目中,必须在porlet(基于java的web组件)环境。
@Component("user") // 至关于在applicationContext.xml中配置对应的bean public class UserDaoOracleImpl implements UserDao { public String userName; public void setUserName(String userName) { this.userName = userName; } @Override public void save() { System.out.println("oracle save"); } @Override public void delete() { System.out.println("oracle delete"); } }
注意:须要在IDE的compiler配置选项中使能注解
后面也能够直接使用AOP自定义的类,并经过配置文件来指定在哪一个切入点添加切面(就是加强函数)。。。
// 定义加强的功能切片类 public class AspectClass { public void checkPrevilidge(){ System.out.println("权限校验"); } public void log(){ System.out.println("日志记录"); } }
前置通知,后置通知每每用于日志的加强功能,咱们来看对应的配置文件