0%

文章字数:125,阅读全文大约需要1分钟

将多个注解合并到一起,这样不用每次都要写若干个重复的注解。

例子

SpringMVC中的注解

  • @RestController
  • @RequestMapping("/user")

可以合并成@PathRestController("/user")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface PathRestController {
@AliasFor("path")
String[] value() default {};

@AliasFor("value")
String[] path() default {};
}

@AliasFor

这是Spring提供的注解,用来为其它属性赋值。
比如

1
2
@AliasFor("path")
String[] value() default {};

就是把value的值也给path赋值。以此实现value和path的值保持一致。


文章字数:57,阅读全文大约需要1分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
/**
* @author colin.cheng
* @version V1.0
* @date Created In 14:51 2019/9/11
*/
public class demo extends JFrame implements KeyListener{

long pressTime=0;
long releasedTime=0;
boolean keyEnable=true;

public static void main(String[] args) {
demo ket = new demo();
}

public demo() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setTitle("KeyEventTest");
this.setSize(300, 300);
this.setVisible(true);
addKeyListener(this);
}

public void keyPressed(KeyEvent e) {
if (keyEnable ==true) {
pressTime = System.currentTimeMillis();
keyEnable=false;
System.out.println("按下");
}
}

public void keyReleased(KeyEvent e) {
releasedTime=System.currentTimeMillis();
long afterTime=releasedTime-pressTime;
System.out.println("总时间"+afterTime+"毫秒");
keyEnable=true;
}

public void keyTyped(KeyEvent e) {
}

}

文章字数:446,阅读全文大约需要1分钟

值传递引用传递

  • 函数内传递是值传递
1
2
3
4
5
6
public static void main(String[] args) {
String test="test1";
Ob that=new Ob();
that.changeTest(test);
System.out.println("test = " + test);
}
1
2
3
public void changeTest(String test){
test="test2";
}

结果:函数内的改变对外部变量无影响

1
test = test1

函数内实参是值传递,即不会对原有对象造成影响
但是,如果传递的是一个对象(本质上是对象的引用地址)就会改变对象,因为即使是值传递,但是传递的地址指向是同一个对象。

  • =符号是引用传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
te a =new te();
te b = a;
//仅修改内容
a.setName("aaa");
b.setName("bbb");
//发现a也修改了
System.out.println("a.getName() = " + a.getName());
// == 符号是判断对象地址是否相同,结果是相同
System.out.print("a==b? ");
System.out.println(a==b);
//输出一下对象的地址,发现果然一样
System.out.println("a = " + a);
System.out.println("b = " + b);
}

内部类

1
2
3
4
5
6
7
8
9
10
11
class te{
private String name;

public String getName(){
return this.name;
}

public void setName(String name){
this.name=name;
}
}

输出结果

1
2
3
4
a.getName() = bbb
a==b? true
a = te@3f91beef
b = te@3f91beef

拓展

这让我想起了之前看到的一个知识点,==Object.equals()的区别。

  • ==判断的是地址是否相同
  • Object.equals()是用来判断对象是否相同的方法,内部默认调用的是==来判断。
1
2
3
4
5
public static void main(String[] args) {
Ob a = new Ob ();
Ob b = new Ob ();
System.out.println("a.equals(b) = " + a.equals(b));
}

结果

1
a.equals(b) = false

如果没有重写equals(),即使使用同一个类的构造方法也显示不同。
重写equals()代码的同时也要重写hashCode()方法,因为要保证equals相等,hashCode一定相等。避免重写equlas()后达到一定条件对象相等,但是hashCode()没变。

hashCode():hash->简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。hashMap也是将key转换成hash存储,如果hashCode相同,则会在同一个节点用链表存储。通过hashCode找到位置,equals获取两个之一。

附 String类重写equals()hashCode()的源码(1.8)

  • equals()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
  • hashCode()
1
2
3
4
5
6
7
8
9
10
11
12
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;

for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

文章字数:97,阅读全文大约需要1分钟

java.lang.Object里有一个finalize()方法,是空的。一旦重写这个方法对象的声明周期就会产生变化。

  1. java.lang.ref.Finalizer.ReferenceQueue:实现finalize()方法的对象都会被加入到ReferenceQueue队列中,因为对象一直被引用,所以无法被GC

  2. finalize():调用对象的终结器方法即可将对象从队列中删除


文章字数:114,阅读全文大约需要1分钟

运算分为逻辑运算与位运算

逻辑运算与位运算

  • &&逻辑和||逻辑或逻辑运算符
  •  按位和按位或位运算符

逻辑运算与短路

  • &&逻辑与:也叫做短路与,当前项为假,它就不往后判断了,直接认为表达式为假
  • ||逻辑或:也叫做短路或,当前项为真,它也不往后判断了,直接认为表达式为真

文章字数:630,阅读全文大约需要2分钟

一、元注解(用于修饰注解)

  1. @Retention指定annotation指定标记注解的存储方式
    值有:

    1
    2
    3
    * `RetentionPolicy.SOURCE` 标记的注释仅保留在源级别中,并由编译器忽略。
    * `RetentionPolicy.CLASS` 标记的注释在编译时由编译器保留,但Java虚拟机`JVM`会忽略。
    * `RetentionPolicy.RUNTIME` 标记的注释由`JVM`保留,因此运行时环境可以使用它。
  2. @Documented注释表明,无论何时使用指定的注释,都应使用Javadoc工具记录这些元素。(默认情况下,注释不包含在Javadoc)

  3. @Target注释标记另一个注释,以限制可以应用注释的Java元素类型。目标注释指定以下元素类型之一作为其值
    可选值有:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    * `ElementType.TYPE` 可以应用于类的任何元素。
    * `ElementType.FIELD` 可以应用于字段或属性。
    * `ElementType.METHOD` 可以应用于方法级注释。
    * `ElementType.PARAMETER` 可以应用于方法的参数。
    * `ElementType.CONSTRUCTOR` 可以应用于构造函数。
    * `ElementType.LOCAL_VARIABLE` 可以应用于局部变量。
    * `ElementType.ANNOTATION_TYPE` 可以应用于注释类型。
    * `ElementType.PACKAGE` 可以应用于包声明。
    * `ElementType.TYPE_PARAMETER`
    * `ElementType.TYPE_USE`
  4. @Inherited注释表明注释类型可以从超类继承。当用户查询注释类型并且该类没有此类型的注释时,将查询类的超类以获取注释类型(默认情况下不是这样)。此注释仅适用于类声明。

  5. @RepeatableJava SE 8中引入的,@Repeatable注释表明标记的注释可以多次应用于相同的声明或类型使用(即可以重复在同一个类、方法、属性等上使用)。

自定义注解

例子

1
2
3
4
5
6
@Documented //javaDoc记录
@Retention(RetentionPolicy.RUNTIME) //运行环境可用
@Target({ElementType.TYPE}) //可用于任何元素
public @interface MyAnn {//自定义一个@Myann(value="")注解
String value() default "";
}

通过反射获取注解信息

  • 判断是否应用的某个注解
    1
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
  • 获取指定类型的注解信息
    1
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
  • 获取所有注解的信息
    1
    public Annotation[] getAnnotations()

文章字数:630,阅读全文大约需要2分钟

工厂模式Factory Pattern

  • 意图:定义一个创建对象的接口,让子类界定实例化那个工厂类。创建过程在子类执行。
  • 解决问题:接口的子类实现各不相同,但是需要同一个实例化入口

抽象工厂Abstract Factory Pattern

  • 意图:将同类产品的工厂类组合起来,形成一个抽象工厂接口。
  • 使用步骤:1.通过抽象工厂的实现类获取工厂类(同类工厂在一起) 2.通过工厂类获取对象实体

单例模式Singleton Pattern

  • 意图:保证类只有一个实例,并且提供全局访问点
  • 实现:判断是否存在实例,存在返回。不存在创建,并用静态变量保存。私有化构造函数,使只有一个入口用于生产实例

建造者模式Builder Pattern

  • 意图:将由多个步骤/节点,组成。每个步骤/节点的内容一样,但是需要不同顺序,数量等组合生成新产品。
  • 实现:1.拥有不同细节实现类 2.按规则生成并保存细节(List)3.将细节拼接成为一个新的对象
  • 例子:StringBuilder

原型模式Prototype Pattern

  • 意图:作为创建模式,和工厂模式不同的是该模式的思想是创建一个对象的原型,原型对象可以复制、克隆为新的对象
  • 实现:原型模式有两种实现
  1. 浅复制:使用Cloneable.clone(),原型继承Cloneable,然后调用super.clone()进行类型转换。浅复制基本数据类型的变量会创建,其它的对象引用指向原型对象。

  2. 深复制:基本类型和引用类型都重新创建

原型implements Cloneable, Serializable

克隆方法

1
2
3
4
5
6
7
8
9
10
11
12
public Object deepClone() throws IOException, ClassNotFoundException {

/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}

文章字数:792,阅读全文大约需要3分钟

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

分类

设计模式大致分为三类(23种):

  • 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。

    这些设计模式提供了一种创建对象时隐藏创建逻辑的方式,而不是使用new直接实例化。使创建实例更加灵活,创建对象的方式更有引导性。

  • 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。

  • 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

遵守原则

  • 开闭原则Open Close Principle
      对扩展开放,对修改关闭。(程序拓展的时候不能修改原有的代码)

  • 里氏代换原则Liskov Substitution Principle

    1
    2
    3
    (1)子类的能力必须大于等于父类,父类可以使用的方法子类都可以使用
    (2)返回值的类型也一样,如父类返回List,子类则可以返回ArrayList
    (3)子类只能抛出父类有的异常
  • 依赖倒转原则Dependence Inversion Principle
    面向接口编程,依赖抽象而不是依赖具体。

  • 接口隔离原则Interface Segregation Principle
    多个隔离的接口比单接口好,降低依赖,降低耦合。

  • 迪米特法则(最少知道原则)Demeter Principle
    实体尽量少的与其它实体之间发生相互作用,系统功能相互独立。

  • 合成复用原则Composite Reuse Principle
    尽量使用合成/聚合的方法,而不是使用继承。
    即原本是父类属性的值剥离出来,比如颜色,专门写一个Color类,其中实现相应的方法。在父类和子类中调用这个类。


文章字数:1118,阅读全文大约需要4分钟

适配器模式(Adapter Pattern)

意图:当一个已经在运行的接口因不兼容而不适应新的接口,把一个接口转换成另一个接口。(解决现有对象不满足新环境的需求)

关键实现:

  1. 新建一个拥有满足新环境需求的接口
  2. 实现此接口,原有的功能引入原接口实现,然后再实现新功能

例子: springSession: 实现了HttpSession接口,内部实现使用的是ExpiringSession,并拓展了其它没有的功能。即session信息都保存在ExpiringSession中(老接口),但是需要实现HttpSession(新接口)的功能。

桥接模式(Bridge Pattern)

意图:创建一段代码时为了保证可拓展性,将抽象部分和实现部分分离,使他们可以单独变换。即一个抽象类,有部分实现了的方法,这些方法组成一个接口,分别不同实现。另外一部分抽象方法留给实现类去实现。

实现:

  1. 创建已经实现的方法的接口,并分别实现
  2. 创建桥接抽象方法,里面提供构造方法选则不同的已实现接口的实现类
  3. 实现抽象方法,使用内置的接口实现定制内容

和视频器模式区别:适配器是现有接口改造成新接口,这个是全新的接口为了方便拓展
相同:都是接口内置其它的实现类,使用接口操作内置的实现类。

组合模式(Composite Pattern)

意图:对象中包含该对象的集合,主要表示对象的一对多关系。总体组成树形结构,可以表示例如董事长->总经理->一堆副总->各部门员工 这样的结构

实现: 创建一个节点对象,其中包含List,list类型为自身类型。并提供增删改查的方法。

装饰器模式(Decorator Pattern)

意图:现有对象添加新的功能,并且不改变原有对象的方法。

实现:

  1. 继承原有对象的接口,保证创建的新对象符合原有接口的方法规则
  2. 内置原有对象,并调用其基本功能,保证功能的一致
  3. 在原有功能上添加自定义的新功能

例子:HttpServletRequestWrapper使用装饰模式增强request的功能(参数拦截之类的)

外观模式(Facade Pattern)

意图:隐藏系统的复杂性,提供一个接口直接完成复杂操作。

实现:将一系列一起使用的接口按照使用顺序调用,再放回唯一值。简而言之就是封装

享元模式(Flyweight Pattern)

意图:减少对象创建的数量,减少内存占用,提高效率。对象复用。

实现:创建存储使用过的对象的列表,当需要时从列表中取对象而不是自己重新创建。当然,有很多细节,比如初始对象数量,最大数量,空闲对象存活时间,对象不够用的策略等。

例子:线程池,连接池

代理模式(Proxy Pattern)

意图:当对象直接访问有问题(对象在远程机器上、安全控制、进程外访问、创建开销大)等情况下,提供一个中间层看似和原有类一样,其实做了中间的转换操作。

实现:

  1. 创建一个新的类,继承需要代理的类的接口,即保持一样的对外方法结构。
  2. 新的类完成中间操作,并访问被代理的类。(远程访问,权限控制,单例等)

不属于7种,也是结构型

过滤器模式(Filter)/标准模式(Criteria)

意图:将挑选规则和挑选动作分离,使一个挑选的动作可以选择不同的规则。

实现:

  1. 定义规则接口,里面有选择规则的函数
  2. 创建类使用规则接口挑选出符合规则的元素
  3. 创建不同的规则(实现规则接口)

例子:java8stream.filter(StringUtil::notBlack)


文章字数:89,阅读全文大约需要1分钟

java调用外部程序可以使用Runtime.getRuntime().exec(),他会调用一个新的进程去执行。

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ExecTest {

public static void main(String[] args) throws IOException, InterruptedException {
  String cmd = "cmd /c dir c:\\windows";
  final Process process = Runtime.getRuntime().exec(cmd);
  printMessage(process.getInputStream());
  printMessage(process.getErrorStream());
  int value = process.waitFor();
  System.out.println(value);
}
//防止输出流缓存不够导致堵塞,所以开启新的线程读取输出流。
private static void printMessage(final InputStream input) {
  new Thread(new Runnable() {
  public void run() {
    Reader reader = new InputStreamReader(input);
    BufferedReader bf = new BufferedReader(reader);
    String line = null;
    try {
    while((line=bf.readLine())!=null) {
    System.out.println(line);
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
 }
  }).start();
}
}