JAVA特点与优势
Java劣势
IDEA编译:
在 IntelliJ IDEA 里,编译方式一共有三种:
• Compile:对选定的目标(Java 类文件),进行强制性编译,不管目标是否是被修改过。
• Rebuild:对选定的目标(Project),进行强制性编译,不管目标是否是被修改过,由于 Rebuild 的目标只有 Project,所以 Rebuild 每次花的时间会比较长。
• Make:使用最多的编译操作。对选定的目标(Project 或 Module)进行编译,但只编译有修改过的文件,没有修改过的文件不会编译,这样平时开发大型项目才不会浪费时间在编译过程中。
Java对象指针:
JAVA中可以说没有指针,因为实际上我们在程序中不会直接操作地址,C++中的*、->操作在JAVA中都不能正常使用。
JAVA中也可以说到处都是指针,因为实际上我们定义一个对象,并给它初始化的时候,这个定义的类对象实际上就是指针。
JVAA中函数的参数是基本类型和对象类型时,他们的处理是不一样的,基本类型传递的是值,而对象类型传递的是引用(类似于指针)。
注意:编译的结果不是生成机器码,而是生成字节码,字节码不能直接运行,必须通过JVM翻译成机器码才能运行。不同平台下编译生成的字节码是一样的,但是由JVM翻译成的机器码却不一样
注意:跨平台的是Java程序,不是JVM。JVM是用C/C++开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的JVM。
Java类库中有很多包:
• 以 java.* 开头的是Java的核心包,所有程序都会使用这些包中的类;
• 以 javax.* 开头的是扩展包,x 是 extension 的意思,也就是扩展。虽然 javax.* 是对 java.* 的优化和扩展,但是由于 javax.* 使用的越来越多,很多程序都依赖于 javax.,所以 javax. 也是核心的一部分了,也随JDK一起发布。
• 以 org.* 开头的是各个机构或组织发布的包,因为这些组织很有影响力,它们的代码质量很高,所以也将它们开发的部分常用的类随JDK一起发布。
组织机构的域名后缀一般为 org,公司的域名后缀一般为 com,可以认为 org.* 开头的包为非盈利组织机构发布的包,它们一般是开源的,可以免费使用在自己的产品中,不用考虑侵权问题,而以 com.* 开头的包往往由盈利性的公司发布,可能会有版权问题,使用时要注意。
import语句与C语言中的 #include 有些类似,语法为:
import package1[.package2…].classname;
package 为包名,classname 为类名。例如:
- import java.util.Date; // 导入 java.util 包下的 Date 类
- import java.util.Scanner; // 导入 java.util 包下的 Scanner 类
- import javax.swing.; // 导入 javax.swing 包下的所有类, 表示所有类
注意:
• import 只能导入包所包含的类,而不能导入包。
• 为方便起见,我们一般不导入单独的类,而是导入包下所有的类,例如 import java.util.*;。
Java类的搜索路径
Java程序运行时要导入相应的类,也就是加载 .class 文件的过程。
假设有如下的 import 语句:
- import p1.Test;
该语句表明要导入 p1 包中的 Test 类。
安装JDK时,我们已经设置了环境变量 CLASSPATH 来指明类库的路径,它的值为 .;%JAVA_HOME%\lib,而 JAVA_HOME 又为 D:\Program Files\jdk1.7.0_71,所以 CLASSPATH 等价于 .;D:\Program Files\jdk1.7.0_71\lib。
Java 运行环境将依次到下面的路径寻找并载入字节码文件 Test.class:
• .p1\Test.class("."表示当前路径)
• D:\Program Files\jdk1.7.0_71\lib\p1\Test.class
如果在第一个路径下找到了所需的类文件,则停止搜索,否则继续搜索后面的路径,如果在所有的路径下都未能找到所需的类文件,则编译或运行出错。
你可以在CLASSPATH变量中增加搜索路径,例如 .;%JAVA_HOME%\lib;C:\javalib,那么你就可以将类文件放在 C:\javalib 目录下,Java运行环境一样会找到。
Java基础语法:
1:在Java中,整型数据的长度与平台无关
自动数据类型转换
自动转换按从低到高的顺序转换。不同类型数据间的优先关系如下:
低--------------------------------------------->高
byte,short,char-> int -> long -> float -> double
字符串拼接:
总结:
1.concat的计算效率要比+的效率高
2.concat只适用于string和string的拼接,+适用于string和任何对象的拼接
3.当在少量的数据拼接时,使用concat和+都行,如果是大量的数据拼接,建议使用StringBuilder或者StringBuffer.
StringBuffer:
String 的值是不可变的,每次对String的操作都会生成新的String对象,不仅效率低,而且耗费大量内存空间。
StringBuffer类和String类一样,也用来表示字符串,但是StringBuffer的内部实现方式和String不同,在进行字符串处理时,不生成新的对象,在内存使用上要优于String。
StringBuffer 默认分配16字节长度的缓冲区,当字符串超过该大小时,会自动增加缓冲区长度,而不是生成新的对象。
StringBuffer不像String,只能通过 new 来创建对象,不支持简写方式
StringBuffer strBuf = new StringBuffer(“www.baidu.com”);
StringBuffer类中的方法主要偏重于对于字符串的操作,例如追加、插入和删除等,这个也是StringBuffer类和String类的主要区别。实际开发中,如果需要对一个字符串进行频繁的修改,建议使用 StringBuffer。
- append() 方法
- deleteCharAt()
- insert() 方法
- setCharAt() 方法
setCharAt() 方法用来修改指定位置的字符
强烈建议在涉及大量字符串操作时使用StringBuffer
StringBuilder:
StringBuilder类和StringBuffer类功能基本相似,方法也差不多,主要区别在于StringBuffer类的方法是多线程安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点
CharSequence:
CharSequence类和String类都可以定义字符串,但是String定义的字符串只能读,CharSequence定义的字符串是可读可写的;
对于抽象类或者接口来说不可以直接使用new的方式创建对象,但是可以直接给它赋值;
CharSequence是一个定义字符串操作的接口,它只包括length()、charAt(int index)、subSequence(int start, int end) 这几个API。
CharSequence转换String:str = cs.toString();
String转换CharSequence:cs = str;
CharSequence和String类型的比较:cs.toString().equal(str);
总结
线程安全:
• StringBuffer:线程安全
• StringBuilder:线程不安全
速度:
一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。
使用环境:
• 操作少量的数据使用 String;
• 单线程操作大量数据使用 StringBuilder;
• 多线程操作大量数据使用 StringBuffer。
Java对象及类:
- public class className {
-
// body of class
- }
- private boolean myFlag;
- static final double weeks = 9.5;
- protected static final int BOXWIDTH = 42;
- public static void main(String[] arguments) {
-
// body of method
- }
Java程序的main() 方法必须设置成公有的,否则,Java解释器将不能运行该类
protected:受保护的
被声明为protected的变量、方法和构造方法能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。
protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected
默认的:不使用任何关键字
不使用任何修饰符声明的属性和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public
访问控制和继承
请注意以下方法继承(不了解继承概念的读者可以跳过这里,或者点击 Java继承和多态 预览)的规则:
• 父类中声明为public的方法在子类中也必须为public。
• 父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。
• 父类中默认修饰符声明的方法,能够在子类中声明为private。
• 父类中声明为private的方法,不能够被继承
重载:
• 声明为final的方法不能被重载。
• 声明为static的方法不能被重载,但是能够被再次声明。
方法覆盖的原则:
• 覆盖方法的返回类型、方法名称、参数列表必须与原方法的相同。
• 覆盖方法不能比原方法访问性差(即访问权限不允许缩小)。
• 覆盖方法不能比原方法抛出更多的异常。
• 被覆盖的方法不能是final类型,因为final修饰的方法是无法覆盖的。
• 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并
抽象和接口:
在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。
接口:
接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。
抽象:
使用abstract关键字来声明
区别:
-
抽象类可以为部分方法提供实现,避免了在子类中重复实现这些方法,提高了代码的可重用性,这是抽象类的优势;而接口中只能包含抽象方法,不能包含任何实现。
-
一个类只能继承一个直接的父类(可能是抽象类),但一个类可以实现多个接口,这个就是接口的优势
综上所述,接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:
• 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
• 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能
异常:
Java异常处理通过5个关键字控制:try、catch、throw、throws和 finally
任何在方法返回前绝对被执行的代码被放置在finally块中
finally {
// block of code to be executed before try block ends
}
throw与throws的比较
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
好的编程习惯:
1.在写程序时,对可能会出现异常的部分通常要用try{…}catch{…}去捕捉它并对它进行处理;
2.用try{…}catch{…}捕捉了异常之后一定要对在catch{…}中对其进行处理,那怕是最简单的一句输出语句,或栈输出e.printStackTrace();
3.如果是捕捉IO输入输出流中的异常,一定要在try{…}catch{…}后加finally{…}把输入输出流关闭;
4.如果在函数体内用throw抛出了某种异常,最好要在函数名中加throws抛异常声明,然后交给调用它的上层函数进行处理
finally创建一个代码块。该代码块在一个try/catch 块完成之后另一个try/catch出现之前执行。finally块无论有没有异常抛出都会执行。finally子句是可选项,可以有也可以无。然而每一个try语句至少需要一个catch或finally子句。
注意:如果finally块与一个try联合使用,finally块将在try结束之前执行。
断言:
断言有两种方法:
• 一种是 assert<<布尔表达式>> ;
• 另一种是 assert<<布尔表达式>> :<<细节描述>>。
assert后面跟个冒号表达式。如果冒烟前为true,则冒号后面的被忽略,否则抛出AssertionError,错误内容为冒号后面的内容
Objects类与Object类
Objects类是final修饰的类,不可继承,内部方法都是static方法,从jdk1.7开始才引入了Objects类
如果一个类没有指定父类,默认就是继承Object类。
Object类里面共有11个方法:
经常用到的equals(),toString()都是直接使用或者重写的Object里面的这些方法;
多线程:
Java集合类
集合和数组不一样, 数组元素既可以是基本类型的值, 也可以是对象(实际上保存的是对象的引用变量), 而集合里只能保存对象(实际上只是保存对象的引用变量, 但通常习惯上认为集合里保存的是对象)
Java的集合类主要由两个接口派生而出:Collection, Map, Collection和Map是Java集合框架的根接口, 这两个接口又包含了一些子接口或实现类.
Collection:
Set:无需集合, 元素不可重复
Queue:队列
List:有序集合, 元素可以重复, ArrayList(非线程安全), Vector(线程安全, 古老的集合, 性能较差, 不建议使用.)
Collections工具类, 它可以将一个ArrayList变成线程安全的.
由于Stack继承了Vector, 因此它是一个非常古老的Java集合类, 它同样是线程安全的, 性能较差的, 因此尽量少用Stack类, 如果程序需要使用”栈”这种数据结构, 则可以考虑使用ArrayDeque.
Map:
Key-Value, key不可重复,
HashMap: 线程不安全, key, value允许为null
Hashtable: 线程安全, key, value不允许为null
HashSet和TreeSet是Set的两个典型实现, 到底如何选择HashSet和TreeSet呢? HashSet的性能总是比TreeSet好,因为TreeSet需要额外的红黑树算法来维护集合元素的次序.只有当需要一个保持排序的Set时, 才应该使用TreeSet, 否则都应该使用HashSet.
对于一般的场景, 程序应该多考虑使用HashMap, 因为HashMap正是为快速查询设计的(HashMap底层其实也是采用数组来存储的key-value), 但如果程序需要一个总是排好序的Map时, 则可以考虑使用TreeMap.
本地联调环境搭建:
用postMan或者其他rest工具调用iac接口,进行本地联调测试时:
注释掉:
//transServerService.decryptData(dsts);
数据库操作:
DriverManager类
DriverManager类处理驱动程序的加载和建立新数据库连接。DriverManager是java.sql包中用于管理数据库驱动程序的类。通常,应用程序只使用类DriverManager的getConnection()静态方法,用来建立与数据库的连接,返回Connection对象:
static Connection getConnection(String url,String username,String password)
指定数据的URL用户名和密码创建数据库连接对象。url的语法格式是:
jdbc:<数据库的连接机制>:<ODBC数据库名>
Connection类
Connection类是java.sql包中用于处理与特定数据库连接的类。Connection对象是用来表示数据库连接的对象,Java程序对数据库的操作都在这种对象上进行。Connection类的主要方法有:
- Statement createStatement():创建一个Statement对象。
- Statement createStatement(int resultSetType,int resultSetConcurrency):创建一个Statement对象,生成具有特定类型的结果集。
- void commit():提交对数据库的改动并释放当前持有的数据库的锁。
- void rollback():回滚当前事务中的所有改动并释放当前连接持有的数据库的锁。
- String getCatalog():获得连接对象的当前目录。
- boolean isClose():判断连接是否已关闭。
- boolean isReadOnly():判断连接是否为只读模式。
- void setReadOnly():设置连接为只读模式。
- void close():释放连接对象的数据库和JDBC资源。
Statement类
Statement类是java.sql包中用于在指定的连接中处理SQL语句的类。数据库编程的要点是在程序中嵌入SQL命令。程序需要声明和创建连接数据库的Connection对象,并让该对象连接数据库。调用类DriverManager的静态方法getConnection()获得Connection对象,实现程序与数据库的连。然后,用Statement类声明SQL语句对象,并调用Connection对象的createStatement()方法,创建SQL语句对象。例如,以下代码创建语句对象sql:
Statement sql = null;
try{
sql = con.createStatement();
}catch(SQLException e){}
ResultSet类
有了SQL语句对象后,调用语句对象的方法executeQuery()执行SQL查询,并将查询结果存放在一个用ResultSet类声明的对象中,例如,以下代码读取学生成绩表存于rs 对象中:
ResultSet rs = sql.executeQuery(“SELECT * FROM ksInfo”);
ResultSet对象实际上是一个由查询结果数据的表,是一个管式数据集,由统一形式的数据行组成,一行对应一条查询记录。在ResultSet对象中隐含着一个游标,一次只能获得游标当前所指的数据行,用next方法可取下一个数据行。用数据行的字段(列)名称或位置索引(自1开始)调用形如getXXX()方法获得记录的字段植 。以下是ResultSet对象的部分方法: - byte getByte(int columnIndex):返回指定字段的字节值。
- Date getDate(int columnIndex):返回指定字段的日期值。
- float getFloat(int columnIndex):返回指定字段的浮点值。
- int getInt(int columnIndex):返回指定字段的整数值。
- String getString(int columnIndex):返回指定字段的字符串值。
- double getDouble(String columnName):返回指定字段的双精度值。
- long getLong(String columnName):返回指定字段的long型整值。
- boolean next():返回是否还有下一字段。
以上方法中的columnIndex是位置索引,用于指定字段,columnName是字段名。
用户需要在查询结果集上浏览,或前后移动、或显示结果集的指定记录,这称为可滚动结果集。程序要获得一个可滚动结果集,只要在获得SQL的语句对象时,增加指定结果集的两个参数即可。例如,以下代码:
Statement stmt = con.createStatement(type,concurrency);
ResultSet rs = stmt.executeQuery(SQL语句)
语句对象stmt的SQL查询就能得到相应类型的结果集。
• int 型参数type决定可滚动集的滚动方式:
• ResultSet.TYPE_FORWORD_ONLY,结果集的游标只能向下滚动。
• ResultSet.TYPE_SCROLL_INSENSITIVE,游标可上下移动,当数据库变化时,当前结果集不变。
• ResultSet. TYPE_SCROLL_SENSITIVE,游标可上下移动,当数据库变化时,当前结果集同步改变。
• int 型参数concurrency决定数据库是否与可滚动集同步更新:
• ResultSet.CONCUR_READ_ONLY,不能用结果集更新数据库中的表。
• ResultSet.CONCUR_UPDATETABLE,能用结果集更新数据库中的表。
例如,以下代码利用连接对象connect,创建Statement对象stmt,指定结果集可滚动,并以只读方式读数据库:
stmt = connect.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY);
可滚动集上另外一些常用的方法如下:
- boolean previous():将游标向上移动,当移到结果集的第一行时,返回false。
- void beforeFirst():将游标移结果集的第一行之前。
- void afterLast():将游标移到结果集的最后一行之后。
- void first():将游标移到第一行。
- void last():将游标移到最后一行。
- boolean isAfterLast():判游标是否在最后一行之后。
- boolean isBeforeFirst():判游标是否在第一行之前。
- boolean isLast():判游标是否在最后一行。
- boolean isFirst():判游标是否在第一行。
- int getRow():获取当前所指的行(行号自1开始编号,结果集空,返回0)。
- boolean absolute(int row):将游标移到row行。
破解数据库中的密码:
选择编译dms项目中的APP2, 然后按照截图填入项目参数,
Main函数中的入参为数据库中的密码, 其它参数为数据库security_unit表中, 如下图2
Spring boot框架:
@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。
Spring注解:
@ComponentScan(basePackages = {“com.hikvision.daf.device.das.controller”,“com.hikvision.daf.device.controller”,“com.hikvision.daf.common.result”})
//自动扫描指定包下所有使用@service, @component, @Controller @Repository的类并注册
https://www.cnblogs.com/cocoxu1992/p/10554889.html
@Repository
用于标注数据访问组件,即DAO组件
@Component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
//https://www.jianshu.com/p/8d3f5cede6bf
遇到的问题
踩到一个坑,有一个接口,在这个接口的实现类里,需要用到@Autowired注解,一时大意,没有在实现类上加上@Component注解,导致了Spring报错,找不到这个类
一旦使用关于Spring的注解出现在类里,例如我在实现类中用到了@Autowired注解,被注解的这个类是从Spring容器中取出来的,那调用的实现类也需要被Spring容器管理,加上@Component
@Component("conversionImpl")
//其实默认的spring中的Bean id 为 conversionImpl(首字母小写)
public class ConversionImpl implements Conversion {
@Autowired
private RedisClient redisClient;
}
介绍
开发中难免会遇到这个这个注解@Component
@controller 控制器(注入服务)
用于标注控制层,相当于struts中的action层
@service 服务(注入dao)
用于标注服务层,主要用来进行业务的逻辑处理
@repository(实现dao访问)
用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
.
@component (把普通pojo实例化到spring容器中,相当于配置文件中的
)
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。
@Resource:
@Resource的作用相当于@Autowired
只不过@Autowired按byType自动注入,
而@Resource默认按 byName自动注入罢了。
@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Resource装配顺序:
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
Bean
@Configuration、@Bean配置详解
(1)@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的< beans>,作用为:配置spring容器(应用上下文)
(2)在@Configuration加上@Bean去注册一个bean 对象,这样我们就不用再去写xml文件去注册bean对象。@Bean标注在方法上(返回某个实例的方法)。
WAR包解压成JAR包及JAR包打包成WAR包:
1、打包 C:\Temp\jar -cvf Blog.war ./*
2、解压 jar -xvf Blog.war
数据库事务处理:
1:@Transactional:这个注解可以标注在类或者方法上, 当它标注在类上时, 代表这个类所有公共非静态的方法都将启用事务功能.建议放在实现类上.
2:@PostConstruct:Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行;
通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
3:数据库隔离级别, 用法:
@Transactional(isolation = Isolation.READ_COMMITTED, timeout = 1, propagation = Propagation.REQUIRED)
Spring提供了四类隔离级别, 分别为未提交读, 读写提交, 可重复读, 串行化.
1.未提交读是最低的隔离级别, 其含义是允许一个事务读取另外一个事务没有提交的数据, 未提交读一中危险的隔离级别.,优点是高并发能力强.
2.读写提交, 是指一个事务只能读取另外一个事务已经提交的数据.不能读取未提交的数据.
3.可重复读,目标是克服读提交中出现不可重复读的现象,待事务提交后才可读取.
4:串行化是数据库最高的隔离级别, 它会要求所有的SQL都会按照顺序执行,这样就可以克服上述隔离级别出现的问题,所以它能够保证数据的一致性.
总结: 在现实中一般而言, 选择隔离级别会以读写提交为主, 它能够防止脏读, 而不能避免重复读,为了克服数据不一致和性能问题, 程序开发者还设计了乐观锁, 甚至不再使用数据库而使用其它手段, 比如Redis.
5:@Transactional自调用失效问题,数据库事务的约定, 其实现原理是AOP, 而AOP的原理是动态代理, 在自调用的过程中, 是类自身的调用,而不是代理对象去调用, 那么就不会产生AOP, 这样Spring就不会把你的代码织入预定的流程中, 于是就失败了.
Redis
1:@EnableCaching:使用Spring缓存注解操作Redis, 为了使用缓存管理器, 需要在Spring boot的配置文件中加入驱动缓存的注解EnableCaching, 这样就可以驱动Spring缓存机制工作了.
抽象类和接口的区别
1.语法层面上的区别
1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2.设计层面上的区别
1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
下面看一个网上流传最广泛的例子:门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:
abstract class Door {
public abstract void open();
public abstract void close();
}
或者:
interface Door {
public abstract void open();
public abstract void close();
}
但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:
1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。
从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。
interface Alram {
void alarm();
}
abstract class Door {
void open();
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//…
}
void close() {
//…
}
void alarm() {
//…
}
}
JAVA虚拟机
.java文件经过javac编译后得到.class文件,称为字节码文件,字节码文件时构成各种平台虚拟机的关键基石,字节码文件包含了java虚拟机指令集和若干其他辅助信息,好多语言比如jRuby Groovy经过编译都会生成字节码文件在虚拟机上运行,所以字节码文件是虚拟机的重要基石.
Javac 编译器
Javac 的编译过程大致可以分为 1 个准备过程和 3 个处理过程:
- 准备过程:初始化插入式注解处理器
- 解析与填充符号表过程:
词法、语法分析:将源码中的字符流转变为标记集合,构造抽象语法树
填充符号表:产生符号地址和符号信息 - 插入式注解处理器的注解处理过程
4.分析与字节码生成过程:
标注检查:对语法的静态信息进行检查
数据流及控制流分析:对程序的动态运行过程进行检查
解语法糖:将简化代码编写的语法糖还原为原来的样子
字节码生成:将前面各个步骤所生成的信息转化为字节码
前端编译与后端编译:
前端编译与优化: 是指java文件编译成.class文件的编译过程
后端编译与优化:把class文件转换成二进制机器码的编译过程.
过滤器(Filter)与拦截器(Interceptor )区别
https://www.cnblogs.com/junzi2099/p/8022058.html
过滤器(Filter)
Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
拦截器(Interceptor)
拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制。拦截器不是在web.xml,比如struts在struts.xml中配置。
拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略
当你提交对Action(默认是.action结尾的url)的请求时,ServletDispatcher会根据你的请求,去调度并执行相应的Action。在Action执行之前,调用被Interceptor截取,Interceptor在Action执行前后执行
SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。
(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的
GC配置:
Debug 常用快捷键
字符串拼接
总结:
1.concat的计算效率要比+的效率高
2.concat只适用于string和string的拼接,+适用于string和任何对象的拼接
3.当在少量的数据拼接时,使用concat和+都行,如果是大量的数据拼接,建议使用StringBuilder或者StringBuffer.
总结
线程安全:
• StringBuffer:线程安全
• StringBuilder:线程不安全
速度:
一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。
使用环境:
• 操作少量的数据使用 String;
• 单线程操作大量数据使用 StringBuilder;
• 多线程操作大量数据使用 StringBuffer。
IDEA快捷键
查看函数实现:
快捷键ctrl+alt+鼠标,点击进去即可。亲试可行,极力推荐
或者右键方法–》go to–》implementations
查看引用:
选中方法名右击,点击Find Usages, 或者 alt + F7
intellij idea查看方法被哪些类引用过?
鼠标光标需要放在方法名字上,然后快捷键Alt+F7,出现弹层点击Yes项,即可出现该方法被哪些类引用过,不是Alt+7,Alt+7快捷键是快速显示当前类中的所有方法的
查找接口的实现类:
IDEA 风格 ctrl + alt +B
查看类或接口的继承关系:
ctrl + h
回退:
Ctrl + Alt +左箭头
按快捷键 Alt+7就能显示当前类中的所有方法、全局常量,方法还包括形参和返回值
重载和覆盖
方法覆盖的原则:
• 覆盖方法的返回类型、方法名称、参数列表必须与原方法的相同。
• 覆盖方法不能比原方法访问性差(即访问权限不允许缩小)。
• 覆盖方法不能比原方法抛出更多的异常。
• 被覆盖的方法不能是final类型,因为final修饰的方法是无法覆盖的。
• 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
• 被覆盖的方法不能为static。如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错误;反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
方法的重载:
前面已经对Java方法重载进行了说明,这里再强调一下,Java父类和子类中的方法都会参与重载,例如,父类中有一个方法是 func(){ … },子类中有一个方法是 func(int i){ … },就构成了方法的重载。
覆盖和重载的不同:
• 方法覆盖要求参数列表必须一致,而方法重载要求参数列表必须不一致。
• 方法覆盖要求返回类型必须一致,方法重载对此没有要求。
• 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)。
• 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
• 父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。
Final
在 Java 中,声明类、变量和方法时,可使用关键字 final 来修饰。final 所修饰的数据具有“终态”的特征,表示“最终的”意思。具体规定如下:
• final 修饰的类不能被继承。
• final 修饰的方法不能被子类重写。
• final 修饰的变量(成员变量或局部变量)即成为常量,只能赋值一次。
• final 修饰的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有 一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用。
• final 修饰的局部变量可以只声明不赋值,然后再进行一次性的赋值。
final 一般用于修饰那些通用性的功能、实现方式或取值不能随意被改变的数据,以避免被误用,例如实现数学三角方法、幂运算等功能的方法,以及数学常量π=3.141593、e=2.71828 等。
事实上,为确保终态性,提供了上述方法和常量的 java.lang.Math 类也已被定义为final 的。
需要注意的是,如果将引用类型(任何类的类型)的变量标记为 final,那么该变量不能指向任何其它对象。但可以改变对象的内容,因为只有引用本身是 final 的。
如果变量被标记为 final,其结果是使它成为常数。想改变 final 变量的值会导致一个编译错误
方法也可以被 final 修饰,被 final 修饰的方法不能被覆盖;变量也可以被 final 修饰,被 final 修饰的变量在创建对象以后就不允许改变它们的值了。一旦将一个类声明为 final,那么该类包含的方法也将被隐式地声明为 final,但是变量不是。
被 final 修饰的方法为静态绑定,不会产生多态(动态绑定),程序在运行时不需要再检索方法表,能够提高代码的执行效率。在Java中,被 static 或 private 修饰的方法会被隐式的声明为 final,因为动态绑定没有意义。
异常:
Java异常处理通过5个关键字控制:try、catch、throw、throws和 finally
任何在方法返回前绝对被执行的代码被放置在finally块中
finally {
// block of code to be executed before try block ends
}
抽象和接口
在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。
接口:
接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。
抽象:
使用abstract关键字来声明
区别:
-
抽象类可以为部分方法提供实现,避免了在子类中重复实现这些方法,提高了代码的可重用性,这是抽象类的优势;而接口中只能包含抽象方法,不能包含任何实现。
-
一个类只能继承一个直接的父类(可能是抽象类),但一个类可以实现多个接口,这个就是接口的优势
综上所述,接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:
• 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
• 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能
生成DUMP的三种方式
1 使用 jmap 命令生成 dump 文件
jmap -dump:live,format=b,file=d:\dump\heap.hprof “pid”
2 使用 jcmd 命令生成 dump 文件
jcmd “pid” GC.heap_dump d:\dump\heap.hprof
3 使用 JVM 参数获取 dump 文件
3.1 XX:+HeapDumpOnOutOfMemoryError
当OutOfMemoryError发生时自动生成 Heap Dump 文件。