为什么说Java具有跨平台特性?

我们知道计算机只认识1,0两种电平的信号,所有信息或者计算指令最终都编码成16进制的机器码,这些机器码作为程序保存于计算机的内存中,由CPU去单个取指令执行直到程序执行完毕。然而计算机能认识的这些机器码确实不是人类善于处理的,因此人们发明了汇编语言,随后使用汇编器(assembler)翻译成为机器码;再随后贝尔实验室发明了C语言,这个就是人类能够理解并创造的高级程序了。同样地,要在CPU上运行,我们必须翻译成机器码,这个由编译器来完成。我们来看下面一句程序:

printf(1+2);

JAVA学习笔记及知识积累-风君雪科技博客

在x86 intel机器上翻译成机器码就是 101000101010 ,intel x86处理器运行这个代码,其中也包含了系统调用的机器码,在这里有两个最关键的东西:1.intel处理器,2.操作系统。这两者的组合我们就称为platform.显然对应不同的处理器和不同的操作系统会有不同的排列组合。对于我们应用开发者来说,我们当然希望我们的应用程序可以很方便地在不同平台上运行。那我们来看看,有哪些方案:

1. 针对不同的处理器汇编问题,我们需要购买host在windows下的针对不同target cpu的汇编器负责转换出能够target到不同cpu的机器码;这个过程叫做交叉编译。并且不同的cpu输出不同的机器码文件,不能混用

2. 针对不同的操作系统,我们printf做的系统调用也会有所不同

JAVA学习笔记及知识积累-风君雪科技博客

但是我们要知道的是交叉编译并且针对不同OS特性做不同的适配,本身就是非常复杂昂贵的,不是每个高级语言开发工程师都能胜任的。这就有了java出现的机会,我们来看看java平台是如何解决这个问题的。

java平台中有一个非常重要的模块: Java Virtual Machine,所有预编译过的bytecode就在这个虚拟机上运行。详细过程如下:

JAVA学习笔记及知识积累-风君雪科技博客

1. printf(1+2)这个代码用java就是System.out.println(1+2),该文本保存为.java文件;

2.使用java编译器将这个.java文件转换成称为bytecode的中间代码,输出为.class文件;

3.这个.class内容本身并不和任何特定平台相关,也就是说任何平台本身是无法直接运行的;

4.这个虚拟机驻留在操作系统的内存中,当虚拟机被喂入这些bytecode时,jvm会识别到我们正在工作的具体平台并且将bytecode最终转换为native machine code

这样你的代码只要编译一次,就能在所有平台上运行!!

因此, 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是什么以及Maven,IDE,Mark,Ant对比

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 archetype

maven提供了很多工程模版,当创建一个新项目时,就可以使用这些模版,自动创建配置好对应的环境

IDE对比:

Eclipse, IntelliJ Idea, myEclips等,我比较喜欢Idea,特别地,java for android新的正式工具也是基于idea设计的。写代码超级爽.使用Idea可以开启一个j2ee hello world程序来学习

JAVA SE/Java EE/ Java ME/JavaFX/Java Card/Java TV/Java DB

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上的应用,嵌入于类似机顶盒的设备中。主要用于手机应用

jdk变迁历史

JAVA学习笔记及知识积累-风君雪科技博客

JAVA程序的编译和运行过程

JAVA学习笔记及知识积累-风君雪科技博客

JAVA运行环境(JRE)

JRE = Java Runtime Environment = JVM + API(Lib)

JRE运行程序时的三项主要功能:由class loader来加载代码,由bytecode verifier来校验代码,由runtime interpreter来执行代码

一句话:由虚拟机来装载编译好的应用程序并且调用相应的指令具体地执行。虚拟机可以理解为在实际的wintel, linux/intel平台上虚拟出一个新的机器,有他自己的指令系统,操作系统API接口,对下会匹配到不同的平台,对上展示的接口是相同的,因此具有跨平台的特征

JAVA开发工具包(JDK)

JDK = JRE+ Tools = JVM + API + Tools 

JAVA学习笔记及知识积累-风君雪科技博客

JDK提供以下主要的开发工具:

java编译器: javac.exe
java执行器: java.exe
文档生成器: javadoc.exe
java打包器: jar.exe
java调试器:jdb.exe
javaw:运行图形界面程序,并且不启控制台
javap:查看类信息及反汇编

javap反汇编后形成的jvm字节码指令和源程序的对应关系:

public class Main {

    public static void main(String[] args) {
        System.out.println("hello, world");
    }
}

J:eclipse-workspaceTType>javap -c outproductionTTypeMain.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
}

面向对象设计的要点:

将客观世界中的各种对象映射为程序中的一个对象
程序的分析和设计都围绕着:

分析有哪些对象类
每个类都有哪些属性,哪些方法
类之间的关系:继承关系和关联关系(比如人有一个手表,老师属于一个学校,属于一个院系)
对象之间互相调用

JAVA Application .VS. Applet

两者的运行环境不同,application是独立的程序,需要执行器(调用虚拟机)来运行;而applet则是嵌在HTML页面中的非独立程序,由专门的appletViewer来运行,或者由web浏览器调用JAVA虚拟机来运行.applet是java最早提出的,是对网页web技术发展的重大改进,从此网页变得丰富,可交互了。要在网页中运行applet必须在系统中有JRE安装,并且要在浏览器中使能java,我们需要将.class以及.html文件放到www服务器上,然后用浏览器访问。从java8开始, applet的运行就收到更加严格的限制。

applet替代方案有flash, silverlight等。甚至现在随着javascript富体验方案以及HTML5提供的丰富功能引入,applet已经慢慢退出历史舞台.

.jar包

.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一些控件,控件中添加事件处理

Java数据类型划分

基本数据类型:byte,short,int,long,float,double,char,boolean存在栈里面

引用数据类型:class,interface,数组:数据本身存在堆里面,但是指针存在栈里面
JAVA学习笔记及知识积累-风君雪科技博客

内存区间之常量池StringBuffer

看下面的代码, 由于常量是存在方法区中的常量池中,值相等的常量就是同一个常量,而字符串”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

package实际上为了解决名字空间,名字冲突,类似php的命名空间,需要和对应存储路径相对应

package pkg1[.pkg2]

根目录由classpath环境变量来确定,如果没有package语句,则是default package.

java jdk实际上提供了很多包,比如: java.applet,java.awt,java.awt.image,java.net,java.util等

import语句

为了使用java中提供的类,需要用import语句来导入所需要的类。

import java.util.Date

导入类后就可以直接使用Date而不用加package的前缀了。

package和物理目录之间的关系

http://www.cnblogs.com/moveofgod/p/3809653.html

package和jar包

第三方的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*/

java访问权限控制修饰符: public/protected/private/默认

JAVA学习笔记及知识积累-风君雪科技博客

需要注意的是,默认如果没有任何修饰符,则成员在同一个包中可以访问.首先要看是否类为public,也就是说是否可以在其他package中能够访问该类,只有有权访问类,再谈能否访问该类的方法。

public class PubClass{
defaultFunction(){} // 默认可以被包内访问
privateFunction(){} // 只能在本类中访问
protectedFunction(){} // 在子类中也能够访问
publicFunction(){} // 任何地方都能够访问
}

private成员的setter/getter(setAttribute/getAttribute)

对于私有的成员,我们一般通过提供setter/getter函数提供读和/或写,好处是能够做合法检查,做量刚的变换等。。如果不提供setAttribute方法则该属性就是只读的

static/final/abstract修饰符

JAVA学习笔记及知识积累-风君雪科技博客

非常常见的,比如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类不能被初始化

接口(interface)

接口就是某种特殊的约定,接口可以被多个类来实现,软件工程趋势于:面向接口的编程,而不是面向实现。

通过接口可以实现不相关的类的相同的行为,而无需考虑这些类之间的层次关系。某种意义上实现了多重继承

接口和类的继承层次无关,同一个接口可以被不同的类来实现。

JAVA学习笔记及知识积累-风君雪科技博客

比如上面这个图中: Flyable这个接口定义了takeoff,fly,land三个接口方法,该接口被Airplane, Bird, Superman三个类来implement了,但是这三个类分别继承于Vehicle, Animal这两个抽象类(abstract class)

JAVA 8中实现了接口函数的默认实现,这样有点像似继承了,不用每个申明implements这个接口的类中都要实现每一个函数。

JAVA学习笔记及知识积累-风君雪科技博客

类成员字段变量和局部变量

字段变量为对象的一部分,因此存在于对象中,也就是堆中;

局部变量为成员函数内声明的变量,存在于栈中

生命周期不同:字段变量随对象创建后一直存在直到对象销毁,而局部变量只有在函数被调用时存在,调用结束则释放内存;

字段变量自动赋初值为0, 局部变量则不会自动初始化,必须显式地初始化。

函数调用时变量的传递(值传递和引用传递)

总的来说,调用对象方法时,java是值传递,即:将表达式的值复制给形式参数。对于引用型变量,传递的值是引用值,不会复制对象实体本身

多态(polymorphism)

多态是指一个程序中相同的名字表示不同的含义。java的多态有两种情形:

编译时多态:重载(overload)是多个同名但是不同签名(参数不同)的不同方法,如:p.sayHello(),p.sayHello(“zhang”);
运行时多态

覆盖override:子类对父类方法进行覆盖,通过动态绑定(dynamic binding),也称之为虚方法调用(virtual method invoking),因为程序调用的是虚的,只有在运行时系统根据调用该方法的实例的类型来决定调用哪个方法
在调用方法时,程序会正确地调用子类对象的方法

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方法

对象清除(garbage collection)

System.gc()调用仅仅建议启动系统的垃圾回收,但是并不能真正做到垃圾的回收。

也可以在子类的finalize()重载实现中去释放资源,类似c++的析构函数。

对于实现了java.lang.AutoCloseable的对象,我们可以使用try范式在代码执行完毕后自动关闭资源:

        try(Scanner scanner = new Scanner(...)) {
            ...
        }

类的类、匿名类

匿名类没有类名,在定义类的同时就生成该对象的一个实例,一次性使用

lambda表达式

相当于匿名函数

(参数)->结果

的形式,类似于javascript的匿名函数

@override是干嘛的

@override是java的伪代码,表示在这里要重写下面的方法,当然也可以没有。写了这个伪代码的好处:

1、可以当注释用,方便阅读;
2、编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错。例如,你如果没写@Override,而你下面的方法名又写错了,这时你的编译器是可以编译通过的,因为编译器以为这个方法是你的子类中自己增加的方法。

注意重载和重写的区别,重载是方法的参数个数或者参数类型不同,导致是不同的方法。而重写是子类对父类相同方法的覆盖重写

POJO(Plain Old Java Object)

POJO = Plain Old Java Object字面意思是普通java对象。其内在含义为没有继承任何类的普通类实例对象

JavaBean

一个pojo类(plain old java object)对象中,如果其字段都有对应的getter和setter,并且有一个无参的构造函数,这类对象我们称之为JavaBean,JavaBean往往由容器来创建,比如tomcat创建javabean,因此需要保证有一个无参构造函数。更多用于数据的临时中转

war文件

war文件是一个适用于tomcat webapp目录下部署的web项目包文件。通常使用一下命令打包和查看,通常包含一堆的jar包文件

jar -cvf blog.war * // 打包
jar -tf blog.war //查看

范型Point<T>

有的时候,具体使用什么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;
}

JVM exception处理

java程序运行时出错的话要么自己try{}catch主动处理,要么通过throws调用交由jvm自行处理。java程序也可以主动抛出异常,层层向上。

JAVA学习笔记及知识积累-风君雪科技博客

throw vs throws

throw在方法体内,由java自己主动抛出异常,而throws则在方法后面紧跟本方法可能抛出的异常,而在别人调用这个异常时,就必须实现catch相应的异常,或者再次主动抛出异常。

JAVA学习笔记及知识积累-风君雪科技博客

反射

类加载器

类加载器负责将.class字节码文件加载到内存中,并为之生成对应的class对象。以便后续使用。

类加载器分以下几类:根类加载器,也称为引导类加载器,负责java核心类的加载,比如system, string等,在JRE的lib目录下rt.jar文件中;
扩展类加载器,负责JRE扩展目录中jar包的加载,在jre lib目录下的ext目录
系统类加载器,负责在JVM启动时加载来自java命令的class文件以及在classpath环境变量中所指定的jar包和类路径

什么是反射

java反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取对象的信息以及动态调用对象方法的功能称为java语言的反射机制。

要使用反射,就必须获得字节码文件

Class.forName/obj.getClass获取字节码

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处理。JAVA学习笔记及知识积累-风君雪科技博客

 使用反射暴力修改private成员

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 
    }
}

使用反射暴力修改private方法访问权限

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内部类

在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.匿名内部类

匿名内部类往往用于从接口定义一个类,仅仅一次使用,没有必要给他一个命名的情形

JAVA学习笔记及知识积累-风君雪科技博客

4.局部内部类

Tomcat体系结构

tomcat作为server run time environment,以web容器的方式提供服务,包括connector(http,https,ajp及其他请求方式)的接入引擎,服务引擎,Host主机服务及host下面的context project等部分组成。

JAVA学习笔记及知识积累-风君雪科技博客

JAVAWeb分层结构

JAVA学习笔记及知识积累-风君雪科技博客

web层负责http请求的接收和响应;web层适用于MVC设计模式,controller实际上就是servlet,view则是JSP,model对于C操作,由input参数创建对应的model并经由service调用DAO持久化,对于R操作,则反过来由DAO层获取到数据呈现到response中.

service层负责核心的业务逻辑;

Dao层则唯一负责和数据库CRUD操作并以model方式返回数据

J2EE web项目运行包体系

JAVA学习笔记及知识积累-风君雪科技博客

JAVA多线程

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
    }
}

JAVA学习笔记及知识积累-风君雪科技博客  

tomcat http请求处理及响应

JAVA学习笔记及知识积累-风君雪科技博客

Servlet

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中不能保存用户相关的数据(相当于是全局互斥数据,会导致线程冲突),否则会导致混乱,注意线程安全

servlet的创建和映射:

可以通过手工在web.xml中定义,或者通过注解@WebServlet(“/myservnet”)的方式来指示编译器完成映射。

JAVA学习笔记及知识积累-风君雪科技博客

单实例,多线程servlet调用模型

JAVA学习笔记及知识积累-风君雪科技博客

servlet配置过程,类似于PHP Laravel的路由配置过程,主要要指定对应的url-pattern,以及对应的servlet类。配置时也存在优先级及覆盖的问题。我们可以在站点级全局web.xml中配置公共路由,也可以在项目级别创建web.xml实现项目级别的路由。

tomcat中静态资源加载过程

当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的本质

JSP本身实质上是一段将来被编译为字节码的java程序,也是一个servlet,类似于PHP的模版引擎(xxx.blade.php),最终编译出来的字节码(php native源码)完成的工作实际上就是拼接java数据到对应前端html中.

JAVA学习笔记及知识积累-风君雪科技博客

JAVA学习笔记及知识积累-风君雪科技博客

servlet的forward和redirect

当tomcat收到一个http请求时,路由到servlet,对应的servlet根据业务逻辑可能需要forward到其他servlet(这是内部转移)或者直接返回一个重定向让浏览器做redirect操作,最终才能完成业务。

JAVA学习笔记及知识积累-风君雪科技博客

如果需要servlet共享数据给jsp,则需要使用forward转发,转发只能转到内部的资源。

往往通过request对象setAttribute增加一个数据,然后forward给jsp来显示数据

request.setAttribute('productList',proList);
RequestDispatcher requestDispatcher = request.getRequestDispatcher('/product/query.jsp');
requestDispatcher.forward(request,response);

JSTL/EL/OGNL(Struts2)

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表达式方式

struts2值域valueStack

虽然在上面的演示中,我们通过request,session,application等域对象可以在页面处理过程中交换数据,但是这类方法更多限定于在jsp页面中访问相关数据,对于如果想在action中访问相关数据,则可以使用struts2框架的值域。action一旦创建,就会生成一个valueStack,他就是一个存放数据的容器。struts2中会将所有的域对象(request,response,application,session)也都存放在了valueStack中,因此最好的方式,咱们都统一称为valueStack方式来处理数据。

jspf文件简化前端资源引用

在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}"/>

JVM内存分布

JAVA学习笔记及知识积累-风君雪科技博客

所谓本地方法区是指非Java语言编写的C或者C++程序需要的栈,而程序计数器则属于线程专有,保存着每个线程运行时场景的堆栈,PC计数器等,可以用于线程的切换

数据库JDBC

jdbc是java定义的一套java访问数据库的接口,数据库的vendor可以根据该接口规范来实现数据库访问驱动,比如mysql的驱动为Mysql-JDBC-impl.jar,Oracle-JDBC-impl.jar, SQL servier-JDBC-impl.jar. 

java程序遵循JDBC规范通过对应的驱动来对实际数据库的访问。

JAVA学习笔记及知识积累-风君雪科技博客

DAO组件

通过上面的JDBC接口,虽然JAVA程序可以直接访问到数据库,但是每次数据库的访问还是比较复杂的过程,比如建立连接,构建SQL,执行SQL,关闭sql statement,关闭jdbc连接。为了责任单一,一般需要抽象出来一层DAO组件,上层程序调用该组件的方法实现数据库操作。

JAVA学习笔记及知识积累-风君雪科技博客

数据库连接池

如上面所描述,每次数据库的操作都需要建立数据库的连接,数据库操作完毕后将连接关闭,而socket建立和关闭是很消耗资源并且缓慢的过程,我们有必要事先创建好这些connection,而数据库访问时,临时从这些connection中取出一个,数据操作完毕后并不真正释放连接,而是将连接对象返回到连接池,供后续应用使用。

JAVA学习笔记及知识积累-风君雪科技博客

过滤器filter

类似于PHP laravel的middleware,我们可以使用filter来对某些servlet进行保护和授权。

JAVA学习笔记及知识积累-风君雪科技博客

监听器listener

我们可以监听servletContext, HttpSession, ServletRequest对象的创建属性更改等事件。

需要注意的是针对监听的对象不同,监听器的作用范围也是不同的,比如针对监听servletContext,则是全局性质的,监听Session的,则只针对单个访问过程周期有效(只要浏览器没有关闭,session就存在),而监听ServletRequest的,则只针对单次请求有效

JAVA学习笔记及知识积累-风君雪科技博客

java web开发xml中三大组件

servlet < filter < listener优先级排序

由于listener优先级最高,最先执行,因此往往把整个项目的初始化数据加载工作放在这里执行

https://blog.csdn.net/sunxianghuang/article/details/52107376

https://blog.csdn.net/xiaojie119120/article/details/73274759

java web中四大作用域及数据对象request/response(PageContext),ServletRequest(整个请求链有效,包括forward和redirect),HttpSession,application(ServletContext)

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应用)

JAVA ObjectOutputStream(序列化和反序列化)实现Serializable接口

类似于python,c, java中也存在对对象持久化的需求.比较典型的例子是tomcat在关闭前会将session内存数据序列化存放到硬盘,而重新启动tomcat则反序列化读取恢复到内存。

Tomcat调试中的详细log使能

在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

微服务/SOA

https://blog.csdn.net/zhengzhaoyang122/article/details/80142955

面向服务架构,和传统的单机单进程搞定一切业务需求不一样,他更强调模块化,层次化,更加容错,系统容易维护,但是也会带来不必要的复杂性。模块之间通过RPC或者REST API调用来通信

JRebel实现j2ee热加载开发

在正常的开发过程中,我们写代码,build,重新部署,使用浏览器检查结果。往往build和重新部署是非常耗时也是频繁发生的,JRebel就是解决这个痛点的,类似于Nodejs、webpack中的HRM模块,在编写前端组件代码时,无需刷新浏览器,代码直接编译并灌入浏览器,这样的开发体验是非常高效完美的。

https://zeroturnaround.com/software/jrebel/pricing/

Struts2

struts实际上就是web层的MVC框架,通过一个前端过滤控制器,截取所有的request,分发到对应的action,在action中可以通过result结果页返回对应的web页面(result实际上就是相当于laravel中的view)

Spring

spring是一个开放源码的设计层面框架,他将面向接口编程思想贯穿始终,是一个分层的JavaSE/JavaEE full-stack一站式轻量级开源框架,他主要为了解决业务逻辑层和其他各层的松耦合关系。这个更加类似于laravel

IOC模式下的Bean配置

通过工厂实现接口与实现的分离。当需求变化时,比如一个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); // 这里就有了初始化的值,由于依赖注入的功劳
} }

DI依赖注入

将spring管理的类中依赖的属性赋值时,通过配置文件来指定spring创建对象的过程就是依赖注入

Bean的scope:单例还是多例(singleton .vs. prototype)

在上面的xml配置代码中,默认创建的对象都是单例模式,也就是只创建了一个,以后都是使用相同的对象。但是很多时候我们必须指定多例模式,比如action对于每次访问都是不同的。

singleton:默认的scope配置,单例模式;

prototype:多例模式

request:应用在web项目中,Spring创建这个类之后,将这个类对象存入到request范围中;

session:应用在web项目中,Spring创建这个类之后,将这个类对象存入到session范围内;

globalsession:应用在web项目中,必须在porlet(基于java的web组件)环境。

通过注解方式声明Bean

@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面向切面编程(类似于python的proxy功能)

JAVA学习笔记及知识积累-风君雪科技博客

后面也可以直接使用AOP自定义的类,并通过配置文件来指定在哪个切入点添加切面(就是增强函数)。。。

// 定义增强的功能切片类
public class AspectClass {
    public void checkPrevilidge(){
        System.out.println("权限校验");
    }
    public void log(){
        System.out.println("日志记录");
    }
}

前置通知,后置通知往往用于日志的增强功能,我们来看对应的配置文件

JAVA学习笔记及知识积累-风君雪科技博客

2018年IEEE Spectrum language Ranking

JAVA学习笔记及知识积累-风君雪科技博客