数据写不进去,其实手动writer.flush(),也可以将数据刷新进去
reader也需要关闭
当有大量数据需要写入时,要分批进行writer.flush一下,否则缓存压力过大
FileWriter extends OutputStreamWriter
OutputStreamWriter extends Writer{
final StreamEncoder se;
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
public void flush() throws IOException {
se.flush();
}
}
class nio.StreamEncoder extends Writer {
}
class BufferedWriter extends Writer {
Writer out;
char cb[];
public void write(String s, int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
int b = off, t = off + len;
while (b < t) {
// 不停往cb里面填数据
s.getChars(b, b + d, cb, nextChar);
// 当数据超出一个临界值时,会自动先out.write(cb, 0, nextChar);
if (nextChar >= nChars)
flushBuffer();
}
}
}
public void flush() throws IOException {
synchronized (lock) {
flushBuffer();
out.flush();
}
}
void flushBuffer() throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar == 0)
return;
out.write(cb, 0, nextChar);
nextChar = 0;
}
}
}
reader = new FileReader(file);
//一个数字,ASCII码,可以强制转换回char
int i = reader.read();
// 连续读三次,可以读到三个字符,若第三个没值,则返回-1,可作为读取结束条件
reader.read()
reader.read()
reader.read()
read(char[] chars)是将数据读入到参数的数组中,返回的是读取到数据的长度
reader = new FileReader(file);
char[] cs = new char[5];
int i = reader.read(cs);
// 若最后只读取到i=2个数据,只会覆盖原数组的前两个数据,后面三个不变
// 当i = -1时候,说明没有读到数据了
i = reader.read(cs);
new FileWriter(“file.txt”,true)
\n代表换行,或\r\n
reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
// BufferedReader关闭,里面的FileReader也会关闭
reader.close();
if(line != null){
//这个就是读取的边界
}
writer = new BufferedWriter(new FileWriter(file));
//写入一行
writer.write("abc");
// 要想换换必须加这个方法
writer.newLine();
writer.write("xyz");
writer.flush();
byte[] bytes = {123,34,45,34};
outputStream = new FileOutputStream(file);
outputStream.write(bytes);
// 字节流无需flush
outputStream.close()
byte[] bs = new byte[5];
FileInputStream inputStream = new FileInputStream(file);
// 读到多少个字节 i,当i为-1时,表示没有读到数据
int i = inputStream.read(bs);
// 指定解码格式,对字节进行解码
String(@NotNull byte[] bytes,
int offset,
int length,
@NotNull java.nio.Charset charset)
in = new BufferedInputStream(new FileInputStream(file));
out = new BufferedOutputStream(new FileOutputStream(file));
byte[] bs = new byte[1024];
// 当len为-1时候表示没读到数据
int len = in.read(bs)
out.write(bs,0,len)
byte[] readStream(final InputStream inputStream) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}
outputStream.flush();
return outputStream.toByteArray();
}
class ByteArrayOutputStream extends OutputStream {
// 数据没有存在文件里,而是这个数组中
byte buf[];
public ByteArrayOutputStream() {
this(32);
}
void write(byte b[], int off, int len) {
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) - b.length > 0)) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(count + len);
// 复制数组
System.arraycopy(b, off, buf, count, len);
count += len;
}
}
class BufferedOutputStream extends FilterOutputStream {
byte buf[];
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
flushBuffer();
out.write(b, off, len);
return;
}
// 将数据先放到buf缓存中
System.arraycopy(b, off, buf, count, len);
count += len;
}
private void flushBuffer() throws IOException {
if (count > 0) {
out.write(buf, 0, count);
count = 0;
}
}
}
class FilterOutputStream extends OutputStream {
OutputStream out;
public void write(int b) throws IOException {
out.write(b);
}
}
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), "gbk");
// 这个“中”字是以gbk形式存储,将来读取的时候也要以gbk取数据
writer.write("中");
char[] cs = new char[1024];
// 解码必须以jdb
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "GBK");
int len = reader.read(cs);
new String(cs,0,len);
// 将数据输出到文件file
PrintWriter writer = new PrintWriter(file);
writer.println("hello");
writer.print("world");
writer.flush();
// 将读到的数据打印到控制台
PrintWriter writer = new PrintWriter(System.out);
writer.flush();
// 线程安全,只能存字符串
Properties extends Hashtable<Object,Object> {
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
}
Properties properties = new Properties();
properties.setProperty("001","张三");
properties.setProperty("002","李四");
String value = properties.getProperty("001");
// 将properties数据写到文件中
PrintWriter writer = new PrintWriter(file);
properties.list(writer);
writer.flush();
// 从文件中加载到properties里
Properties properties = new Properties();
FileInputStream inputStream = new FileInputStream(file);
properties.load(inputStream);
// Person要实现序列化接口,加一个序列化id,不加序列化id,当类变化例如增加字段时,反序列化会报错
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(new Person());
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
Object o = inputStream.readObject();
// 一般使用集合,集合实现了序列化接口
list.add(person1)
list.add(person2)
outputStream.writeObject(list);
file.exists()
file.createNewFile()
file.mkdir()
//创建多级目录
file.mkdirs()
//删除不走回收站,删除文件和目录最后一层
file.delete()
//获取文件绝对路径
file.getAbsolutePath()
//new File("b.txt"),相对路径是相对项目名作为根路径
//获取相对路径
file.getPath()
file.getName()
//文件大小,多少字节
file.length()
File[] files = file.listFiles();
boolean canExecute = file.canExecute();
boolean canRead = file.canRead();
boolean canWrite = file.canWrite();
boolean hidden = file.isHidden();
//体现层级关系递归打印目录树,注意space的使用
int space = 1;
void print(File file) {
space ++;
File[] files = file.listFiles();
for (File f : files) {
printSpace(space);
System.out.println(f);
if (f.isDirectory()) {
printFile(file);
}
}
space --;
}
// 递归删除目录下的所有文件和目录,注意,只能删除空目录
void deleteDirector(File file) {
if (file == null || !file.exists()) {
return;
}
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
deleteDirector(f);
}else {
f.delete();
}
}
//这步是点睛之笔
file.delete();
}
Path path= Paths.get(new URI(myPath));
byte[] cLassBytes= Files.readAllBytes(path);
对其第一次使用时:
new Dog();
Class<?> clazz = Class.forName("com.file.Dog");
Dog.staticAttribute
Dog.staticMethod
任意一个类,都需要由加载它的类加载器和这个类本身一同确定其在java虚拟机中的唯一性
Java类随着它的类加载器具备了一种带有优先级的层次关系。例如Object放在rt.jar中,无论哪一个类加载器加载这个类,最终都是委派给模型最顶端的启动类加载器加载
// 应用程序类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> loadClass(String name) {
// 检查是否已被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// Returns a class loaded by the bootstrap class loader;
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
// 这里并没有打印异常
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
return c;
}
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("spring-config.xml");
还没找到答案
class ClassUtils {
static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
// 方式一获得ClassLoader
cl = Thread.currentThread().getContextClassLoader();
}
if (cl == null) {
// 方式二
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// 方式三
cl = ClassLoader.getSystemClassLoader();
}
}
}
}
Class<?> clazz = Class.forName("com.file.Dog");
Object object = new Object();
Class<?> clazz = object.getClass();
Class<Dog> clazz = Dog.class;
Class<Integer> integerClass = int.class;
Class<Integer> integerClass1 = Integer.TYPE;
ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
Class<?> clazz = defaultClassLoader.loadClass("com.file.Dog");
Class<Dog> dogClass = Dog.class;
Dog dog = dogClass.newInstance();
Constructor<Dog> constructor1 = dogClass.getConstructor();
Constructor<Dog> constructor2 = dogClass.getConstructor(String.class);
Dog dog1 = constructor1.newInstance();
Dog dog2 = constructor2.newInstance("小黄");
Dog dog = new Dog();
boolean b = dog instanceof Dog;
boolean b1 = Dog.class.isInstance(dog);
// 虽然Animal是Dog的父类,但是类型不完全一致,返回false
boolean b2 = Dog.class.isAssignableFrom(Animal.class);
Class<Dog> dogClass = Dog.class;
// 只能获得public属性
Field[] fields = dogClass.getFields();
// 不仅可以获得public,还可以获得private等所有属性,但不包括父类属性
Field[] declaredFields = dogClass.getDeclaredFields();
Dog dog = new Dog();
Class<? extends Dog> dogClass = dog.getClass();
Field dogName = dogClass.getDeclaredField("dogName");
// 必须设置为true,否则私有没权限访问
dogName.setAccessible(true);
dogName.set(dog,"小黄");
Object nameString = dogName.get(dog);
// 获得类的指定public方法
// 获取所有public修饰的方法:getMethods
// 获得所有方法,包括private,getDeclaredMethods
Method say = dogClass.getMethod("say", String.class);
Object returnValue = say.invoke(dog, "汪汪汪");
Method mainMethod = Class.forName("com.Test")
.getMethod("main", String[].class);
// 注意invoke的第一个参数为空,因为没有创建对象
mainMethod.invoke(null, (Object)new String[]{"111", "222", "333"});
Class<BookService> bookService2Class = BookService.class;
Annotation[] annotations = bookServiceClass.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Scope) {
Scope annotation = (Scope) annotation;
String value = ((Scope) annotation).scopeName();
System.out.println(value);
}
}
final class Class<T> implements AnnotatedElement {
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
}
final class Method extends Executable {
}
abstract class Executable extends AccessibleObject implements Member {
}
class AccessibleObject implements AnnotatedElement {
}
final class Field extends AccessibleObject implements Member {}
Class<?> animalClazz = dogClass.getSuperclass();
Object animal = animalClazz.newInstance();
Field[] fields = animalClazz.getDeclaredFields();
fields[0].setAccessible(true);
Object age = fields[0].get(animal);
System.out.println(age);
// "com.Dog"
String name = dogClass.getName();
Class<?> superclass = dogClass.getSuperclass();
Class<?>[] interfaces = dogClass.getInterfaces();
boolean b = dogClass.isInterface();
// "Dog"
String simpleName = dogClass.getSimpleName();
// 所有数据类型都继承自Type,Class就是继承自Type的
Type type = TypeTest.class;
// 下面演示获得
Foo<Integer> foo = new Foo<Integer>(){};
// com.fanxin.Foo
Type mySuperClass = foo.getClass().getGenericSuperclass();
// type = class java.lang.Integer
Type type = ((ParameterizedType)mySuperClass).getActualTypeArguments()[0];
public class TypeVariableTest<T extends Number & Serializable> {
private T t;
}
Field fieldT = TypeVariableTest.class.getDeclaredField("t");
// T
TypeVariable typeVariable=(TypeVariable)fieldT.getGenericType();
Type[] types=typeVariable.getBounds();
for (Type type:types){
// class java.lang.Number
// interface java.io.Serializable
System.out.println(type);
}
// T
System.out.println(typeVariable.getName());
// class com.fanxin.TypeVariableTest
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
public class WildcardTypeTest {
private List<? extends String> listStr;
private List<? super String> b;
public static void testGetBounds(String field) throws NoSuchFieldException {
Field f = WildcardTypeTest.class.getDeclaredField(field);
// java.util.List extends java.lang.String>
Type type = f.getGenericType();
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] types = parameterizedType.getActualTypeArguments();
WildcardType wildcardType = (WildcardType) types[0];
{
Type[] types = wildcardType.getUpperBounds();
for (Type type : types) {
System.out.println(type);
}
types = wildcardType.getLowerBounds();
for (Type type : types) {
System.out.println(type);
}
}
}
public static void testGetUpperBounds() throws NoSuchFieldException
{
//listStr
//class java.lang.String
testGetBounds("listStr");
// b
// class java.lang.Object
// class java.lang.String
testGetBounds("b");
}
public static void main(String[] args) throws NoSuchFieldException {
testGetUpperBounds();
}
}
public class GenericArrayTypeBean<T> {
// 属于 GenericArrayType
List<String>[] pTypeArray;
// 属于 GenericArrayType
T[] vTypeArray;
// 不属于 GenericArrayType
List<String> list;
// 不属于 GenericArrayType
String[] strings;
public static void main(String[] args) throws NoSuchFieldException {
Field field = GenericArrayTypeBean.class.getDeclaredField("pTypeArray");
// java.util.List[]
GenericArrayType genericType = (GenericArrayType)field.getGenericType();
// java.util.List
Type genericComponentType = genericType.getGenericComponentType();
}
}
public class TypeTest {
class Person<T> {
}
class Student extends Person<TypeTest> {
}
public static void main(String[] args) {
new TypeTest().test();
}
public void test() {
System.out.println("Student.class.getSuperclass()\t"
+ Student.class.getSuperclass());
System.out.println("Student.class.getGenericSuperclass()\t"
+ Student.class.getGenericSuperclass());
System.out.println();
System.out.println("TypeTest.class.getSuperclass()\t"
+ TypeTest.class.getSuperclass());
System.out.println("TypeTest.class.getGenericSuperclass()\t"
+ TypeTest.class.getGenericSuperclass());
System.out.println();
System.out.println("Object.class.getGenericSuperclass()\t"
+ Object.class.getGenericSuperclass());
System.out.println("Object.class.getSuperclass()\t"
+ Object.class.getSuperclass());
System.out.println();
System.out.println("void.class.getSuperclass()\t"
+ void.class.getSuperclass());
System.out.println("void.class.getGenericSuperclass()\t"
+ void.class.getGenericSuperclass());
System.out.println();
System.out.println("int[].class.getSuperclass()\t"
+ int[].class.getSuperclass());
System.out.println("int[].class.getGenericSuperclass()\t"
+ int[].class.getGenericSuperclass());
// Student.class.getSuperclass() class com.fanxin.TypeTest$Person
// Student.class.getGenericSuperclass() com.fanxin.TypeTest.com.fanxin.TypeTest$Person
//TypeTest.class.getSuperclass() class java.lang.Object
//TypeTest.class.getGenericSuperclass() class java.lang.Object
//Object.class.getGenericSuperclass() null
//Object.class.getSuperclass() null
//void.class.getSuperclass() null
//void.class.getGenericSuperclass() null
//int[].class.getSuperclass() class java.lang.Object
//int[].class.getGenericSuperclass() class java.lang.Object
}
}
public class FieldTest {
class Person<T> {
public List<CharSequence> charSequenceList;
public String str;
}
class Student extends Person<FieldTest> {
}
public static void main(String[] args) throws NoSuchFieldException {
new FieldTest().test1();
}
public void test1() throws NoSuchFieldException {
{
Field field=Student.class.getField("str");
// class java.lang.String
// class java.lang.String
System.out.println(field.getType());
System.out.println(field.getGenericType());
}
{
Field field=Student.class.getField("charSequenceList");
// interface java.util.List
// java.util.List
System.out.println(field.getType());
System.out.println(field.getGenericType());
}
}
}
public class MethodTest {
static class Person<T> {
}
static class Student extends Person<MethodTest> {
public Student(List<CharSequence> list)
{
}
public Integer test(List<CharSequence> list)
{
return null;
}
}
public static void main(String[] args) throws NoSuchMethodException {
new MethodTest().test1();
}
public void test1() throws NoSuchMethodException {
{
Method method=Student.class.getMethod("test",List.class);
// interface java.util.List
Class type1=method.getParameterTypes()[0];
// java.util.List
Type type2=method.getGenericParameterTypes()[0];
}
{
Constructor constructor=Student.class.getConstructor(List.class);
// interface java.util.List
Class type1=constructor.getParameterTypes()[0];
// java.util.List
Type type2=constructor.getGenericParameterTypes()[0];
}
}
}
public class MethodParameterTests {
public static class TestItem
{
public TestItem(List<String> list)
{
}
public void m1(List<Integer> intParam)
{
}
}
public static void main(String[] args) throws NoSuchMethodException {
new MethodParameterTests().test();
}
public void test() throws NoSuchMethodException {
{
Method method=TestItem.class.getMethod("m1",List.class);
// method 'm1' parameter 0
MethodParameter parameter=MethodParameter.forMethodOrConstructor(method,0);
// interface java.util.List
Class<?> parameterType = parameter.getParameterType();
// java.util.List
Type genericParameterType = parameter.getGenericParameterType();
}
{
Constructor constructor=TestItem.class.getConstructor(List.class);
MethodParameter parameter=MethodParameter.forMethodOrConstructor(constructor,0);
// interface java.util.List
Class<?> parameterType = parameter.getParameterType();
// java.util.List
Type genericParameterType = parameter.getGenericParameterType();
}
}
}
public class Test {
static class ExtendsList extends ArrayList<CharSequence> {
public List charSequenceList;
public String[] arrayType;
// 属于 GenericArrayType
List<String>[] pTypeArray;
public String charSequenceParameter(List<String> list,String s){
return "";
}
}
public static void main(String[] args) throws Exception {
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// class com.fanxin.Test$ExtendsList
Type type = extendsListResolvableType.getType();
// class com.fanxin.Test$ExtendsList
Class<ExtendsList> extendsListClass = ExtendsList.class;
// class com.fanxin.Test$ExtendsList
Type t = extendsListClass;
}
{
Field charSequenceList = ExtendsList.class.getField("charSequenceList");
ResolvableType fieldResolvableType = ResolvableType.forField(charSequenceList);
// interface java.util.List
Type type = fieldResolvableType.getType();
// interface java.util.List
Type fieldGenericType = charSequenceList.getGenericType();
}
{
Method method = ExtendsList.class.getMethod("charSequenceParameter", List.class,String.class);
// 封装方法的第一个参数
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0);
ResolvableType resolvableType = ResolvableType.forMethodParameter(methodParameter);
// java.util.List
Type type = resolvableType.getType();
// class java.lang.String
Type genericParameterType = method.getGenericParameterTypes()[1];
// java.util.List
Type genericParameterType1 = method.getGenericParameterTypes()[0];
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// class com.fanxin.Test$ExtendsList
Class<?> rawClass = extendsListResolvableType.getRawClass();
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
ResolvableType superType = extendsListResolvableType.getSuperType();
// java.util.ArrayList
Type type = superType.getType();
// class java.util.ArrayList
Class<?> rawClass = superType.getRawClass();
}
{
ResolvableType extendsListResolvableType = ResolvableType.forClass(ExtendsList.class);
// as向上取接口或父类
ResolvableType resolvableType = extendsListResolvableType.as(ArrayList.class);
// java.util.ArrayList
Type type = resolvableType.getType();
// class java.util.ArrayList
Class<?> rawClass1 = resolvableType.getRawClass();
// class java.util.ArrayList
ResolvableType generic = resolvableType.getGeneric();
// interface java.lang.CharSequence
Type type3 = resolvableType.getGeneric().getType();
// interface java.lang.CharSequence
Class<?>[] classes = resolvableType.resolveGenerics();
}
{
Field arrayType = ExtendsList.class.getField("arrayType");
ResolvableType resolvableType = ResolvableType.forField(arrayType);
// true
boolean b = resolvableType.isArray();
// java.lang.String
ResolvableType componentType = resolvableType.getComponentType();
// class java.lang.String
Type type = resolvableType.getComponentType().getType();
// class java.lang.String
Type componentType = ((Class) arrayType.getGenericType()).getComponentType();
Type type1 = resolvableType.getType();
// ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.GenericArrayType
// String[] 不属于GenericArrayType
// List[]属于
GenericArrayType type11 = (GenericArrayType) type1;
}
}
}
public interface InterfaceMy {
void doSomething();
void somethingElse(String arg);
}
public class RealObj implements InterfaceMy {
@Override
public void doSomething() {
System.out.println("doSomething");
}
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse" + arg);
}
}
public class DynamicProxyHandler implements InvocationHandler {
private Interface proxied;
public DynamicProxyHandler(Interface proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收保护费");
// method.invoke 是调用对象本身方法
return method.invoke(proxied,args);
}
}
RealObj realObj = new RealObj();
Interface i =(Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{Interface.class},
new DynamicProxyHandler(realObj)
);
i.doSomething();
注意需要参数:
class数组,因为realobj可能实现多个接口,获得所有接口则realobj.getClass().getInterfaces()
ConnectionManager.benigTransction(conn);
param = method.invoke(obj, args);
// 提交事务
ConnectionManager.endTransction(conn);
return param;
public int id();
public String description() default "no description";
使用时,不需要value = “xxx”,直接用就是了
驼峰、常量大写下划线隔开、抽象类以abstract或base开头、异常类以exception结尾、测试类要以测试的类名开头,以Test结尾、
部分框架解析会引起序列化错误。
基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),RPC框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常
使用小写、使用单数
缩写之后连意思都找不到了要避免
不要
Impl
get、list、count、save/insert、remove/delete、update
数据对象、数据传输对象、展示对象
数据对象:xxxDO,xxx为数据表名
展示对象:xxxVO,xxx为网页名
数据传输对象:xxxDTO,xxx为业务领域相关的名称
pojo是DO、DTO、VO的总称
未经预定义的常量,要避免
后面不能是小l,应该是大L
不可以,应该分类
子工程内,应用内、跨应用
应用内
左大括号前不换行,后换行
不出现空格
必须加空格
空格,例如 a == b a = b
必须加空格
运算符与上下文一起换行,第二行相对第一行缩进四个空格
逗号后面必须空格
代码逻辑分清红花和绿叶,个性和共性
绿叶逻辑单独出来成为额外方法,共性逻辑抽取成为共性方法
使用对象点. ,直接使用类名点
不提倡
左边最好是常量和确定值
使用equals
包装类型
基本类型
包装类型
不可以
构造方法不要加任何业务逻辑,可以放在init中
tostring()
公有方法或保护方法 > 私有方法 > getter/setter 方法
使用stringbuilder,因为string会每次循环都出来一个stringbuilder对象,造成内存浪费
同时重写hashcode
因为string重写了hashcode和equals方法
sublist只是其一个视图,是arraylist的内部类,
list.toArray(array),传入array大小为list.size()
不能直接删除集合里的元素,必须iterator.remove
处理相等时候的情况
不要使用keyset,因为遍历了两次
使用entryset
jdk8:
// 传统的Map迭代方式
for (Map.Entry entry : infoMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// JDK8的迭代方式
infoMap.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
除了hashmap允许key为空,其他都不行,hashmap和treemap允许value为空,hashtable和concurrenthashmap不允许value为空,hashtable线程安全,concurrenthashmap部分安全,剩下两个不安全
arraylist是稳定,无序
treeset是有序,稳定
hashmap是无序,不稳定
不允许显示创建线程
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式
Executors创建的线程池存在OOM的风险,使用了linkedblockqueue,LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE
private static ExecutorService executor = new ThreadPoolExecutor
(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
System.out.println( TimeUnit.DAYS.toHours( 1 ) );
System.out.println( TimeUnit.HOURS.toSeconds( 1 ));
System.out.println( TimeUnit.HOURS.convert( 3 , TimeUnit.DAYS ) );
TimeUnit.SECONDS.sleep( 5 );
相当于
Thread.sleep( 5 * 1000 );
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
Date:Instant
LocalDateTime:Calendar
DateTimeFormatter:SimpleDateFormate
官方解释:简单,漂亮,强壮,不变,线程安全
对象锁更好
保持一致的加锁顺序
缓存加锁,数据库加锁
如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次 数不得小于 3 次
ScheduledExecutorService
多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
catch 异常,确保 countDown 方法被执行到
不能
不仅不会回滚,而且会继续往下执行
runtimeexception,如果抛出exception事务不会回滚,因为spring捕获的是runtimeexception
a方法,只有加在调用链上的第一个方法才有效,c方法不需要加transaction,但是a,b,c三个方法必须都是public方法
手动回滚事务
a方法调用b、c方法,后抛出runtimeexception,并不会影响b,c方法自己事务,且b,c自己事务是独立的
会,b,c加入外围事务
不是,a连带c都会回滚
依然回滚,外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚
b,c不会加入a的事务,a抛异常不会导致b,c回滚
会,c抛出异常回滚,异常继续抛出被外围a感知,a也回滚
所有以REQUIRED加入a的都会回滚
REQUIRED即使在外围捕获异常,依然会回滚
REQUIRES_NEW被捕获了就不会导致外围事务回滚
由于异常被捕获了,spring感知不到异常,所以不会回滚,例外就是;b事务是REQUIRED,外围事务a方法里面捕获b,依然会导致a和b两个事务回滚
b抛出异常,a感知到b的异常,导致a,b,c都回滚,这点像REQUIRED
b抛出异常,外围a捕获异常,那么a,c不会回滚,这点像REQUIRES_NEW
外围抛出异常,NESTED会回滚,而REQUIRES_NEW不会
多线程中性能不高,使用ThreadLocalRandom代替
多线程使用threadlocalrandom需要在每个线程ThreadLocalRandom.current().nextInt(100)
一写多读
多写
LongAdder
static
每个case使用return或break终止,必须最后包含一个default语句放到最后
大括号
”等于”
容易产生等值判断被“击穿”的情况,使用大于或小于的区间 判断条件来代替
卫语句、策略模式、状态模式
复杂语句
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) { ... }
dao层不需要,都到最下层了,不太可能出问题
/*内容/,不要//
创建者和创建日期
预编译不要放在方法体内
不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);
System.currentTimeMillis(); 而不是 new Date().getTime();
会
finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句
(Log4j、Logback)中的 API
而应依赖使用日志框架 SLF4J 中的 API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
15
APPname_logType_logname.log
如果日志级别是 warn,上述日志不会打印,但是会执行里面的操作浪费了系统资源
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
LOG.info("info");
LOG.warn("warn");
LOG.error("error");
出现:
/home/info.log文件中会打印info,warn,error三行日志;
/home/warn.log文件中会打印warn,error两行;
/home/error.log文件中只会打印error一行日志。
解决:设置 additivity=false
案发现场信息和异常堆栈信息
logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
error、warn、info、debug
debug
进行权限控制校验
脱敏
select where id = ?
用户输入 1 or 1=1,结果变成
id = 1 or 1=1
#传入的数据会自动加一个双引号,$是直接拼接
这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题
进行有效性认证
假如博客园有个加关注的GET接口,blogUserGuid参数很明显是关注人Id,如下:
http://www.cnblogs.com/mvc/Follow/FollowBlogger.aspx?blogUserGuid=4e8c33d0-77fe-df11-ac81-842b2b196315
那我只需要在我的一篇博文内容里面写一个img标签:
那么只要有人打开我这篇博文,那就会自动关注我
解决:服务端生成一个Token,用户请求时候必须携带token
重放
发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其 它用户,并造成短信平台资源浪费
命名:is_xxx
数据类型:unsigned tinyint
不能
不需要,xxxDO也是单数,包名也不使用复数
pk_字段名
uk_字段名
idx_字段名
decimal
char类型
id,gmt_create,gmt_modified
字段长度短,不是频繁修改
单表行数超过 500 万行或者单表容量超过 2GB
建立唯一索引
索引长度
order by 字段作为索引的一部分,但是放在最后面
count*会统计值为null的行,count列名不会
count(distinct col) 计算该列除 NULL 之外的不重复行数
0
0
null
isnull()
因为NULL 与任何值的直接比较都为 NULL
直接返回,避免执行后面的分页语句
在应用层解决
存储过程难以调试和扩展,更没有移植性
select,确认无误后才能执行
在resultmap中写明映射关系
gmt_modified
缓存回滚,消息补偿,统计修正
开放接口层:可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行 网关安全控制、流量控制等。
终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染, JSP 渲染,移动端展示等。
Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
Service 层:相对具体的业务逻辑服务层。
Manager 层:通用业务处理层,它有如下特征: 1) 对第三方平台封装的层,预处理返回结果及转化异常信息; 2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理; 3) 与 DAO 层交互,对多个 DAO 的组合复用。
DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口
在 DAO 层,产生的异常类型有很多,无法用细粒度的异常进 行 catch,使用 catch(Exception e)方式,并 throw new DAOException(e),日志在 Manager/Service 层一定需要捕获并打印到日志文件中去
Query
Map
groupid:com.公司.业务线.业务线
artifactid:产品名-模块名
version:起始版本必须为1.0.0
操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接,在高并发访问下,服 务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上 调小此等待值
一样大,避免在 GC 后调整堆 大小带来的压力
主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对 应于一个 fd,
当并发连接数很大时很容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
【强制】在需求分析阶段,如果与系统交互的 User 超过一类并且相关的 User Case 超过 5 个, 使用用例图来表达更加清晰的结构化需求。
【强制】如果某个业务对象的状态超过 3 个,使用状态图来表达并且明确状态变化的各个触发 条件。
说明:状态图的核心是对象状态,首先明确对象有多少种状态,然后明确两两状态之间是否存
在直接转换关系,再明确触发状态转换的条件是什么。
正例:淘宝订单状态有已下单、待付款、已付款、待发货、已发货、已收货等。比如已下单与
已收货这两种状态之间是不可能有直接转换关系的。
【强制】如果系统中某个功能的调用链路上的涉及对象超过 3 个,使用时序图来表达并且明确 各调用环节的输入与输出。
说明:时序图反映了一系列对象间的交互与协作关系,清晰立体地反映系统的调用纵深链路。
【强制】如果系统中模型类超过 5 个,并且存在复杂的依赖关系,使用类图来表达并且明确类
之间的关系。
说明:类图像建筑领域的施工图,如果搭平房,可能不需要,但如果建造蚂蚁 Z 空间大楼,肯
定需要详细的施工图。
【强制】如果系统中超过 2 个对象之间存在协作关系,并且需要表示复杂的处理流程,使用活
动图来表示。
说明:活动图是流程图的扩展,增加了能够体现协作关系的对象泳道,支持表示并发等。
关联:例如用户转账,用户是角色,转账是用例,角色用一个小人表示,用例用一个椭圆表示,角色一个箭头指向用例,
包含:是指两个用例之间的关系,例如查询包含打印回执,提款和转账都包含打印回执,那么查询可以使用一个虚线箭头指向回执,如果两个用例有大量一致的功能,可以将这些功能分解到一个用例中,使用包含关系。
角色也可以执行被包含的用例,例如:取消订单包含查询订单,那么角色当然既可以取消订单,也可以查询订单。如果一个用例功能太多,也可以使用包含
扩展:把新的行为插入到已有用例中的方法,只在特定的条件发生,扩展用例才会被执行,例如:VIP打折用例是订购货物用例的扩展用例,画法是扩展用例一个虚线箭头指向基础用例,与包含不同的是《extend》与《include》
泛化:类似于继承,例如:父用例是预定,子用例是电话预定和网上预定,任何父用例出现的地方子用例也可以出现,使用箭头加三角形,子用例指向父用例
先画不同维度的状态图,再合并
行为顺序
一个类操作或者引起转换的触发事件
纵轴是时间轴,时间越向下越大
对象放在横轴顶部
生命线是一条垂直的虚线
消息是;两个对象之间的通信,发送方指向接收方
激活指生命线被占用以完成某个任务,钝化指生命线处于空闲状态
对象激活时将对象的生命线拓宽为矩形来表示的
public <T> T getBean(String name, Class<T> requiredType)
Class<BookService> bookServiceClass = BookService.class;
// 类
class Test<T> {
}
// 方法
public <T> T getBean(String name, Class<T> requiredType)
interface Generator<T> {
T next();
}
// 会报错
class CoffeGenerator implements Generator<T> {
}
// 在使用接口时候指定具体类型,而类在创建对象时指定具体类型
class CoffeGenerator implements Generator<Coffee> {
}
// 不指定具体类型,将指定类型任务交给子类
public class BasicGenerator<T> implements Generator<T> {
// 会报错,要去掉static,类上的泛型不能用在静态方法或属性上
public static T f(T t){
return t
}
}
public class New {
public static <T> List<T> list(){
return new ArrayList<T>();
}
public static void f(List<? extends Fruit> fruits) {
}
public static void main(String[] args) {
List<Object> list = New.list();
List<Apple> list1 = New.list();
List<? extends Fruit> list2 = New.list();
list.add(new Object());
list1.add(new Apple());
list2.add(new Fruit()); // 添加报错
list2.add(new Apple()); // 添加报错
list2.get(0).ff(); // 没问题,ff()是Fruit的方法
f(list1);
f(list2);
List<? super Apple> apples = new ArrayList();
apples.add(new Apple());
apples.add(new Jonathan()); // 没问题
apples.add(new Fruit()); // 报错
}
}
// 元素反作用于泛型类,之前是先确定类上的类型,后确定类里面的元素类型
public static <T> Plate<T> create(T item) {
return new Plate<>(item);
}
public class Plate<T> {
private T item;
public Plate(T t){item=t;}
public void set(T t){
Object o = new Object();
item=t;
}
public T get(){return item;}
}
class TaskWithResult implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1024;
}
}
interface ExecutorService {
<T> Future<T> submit(Callable<T> task)
}
Future<Integer> submit = executorService.submit(new TaskWithResult())
interface Future<V> {
V get()
}
class Manipulation<T> {
private T obj;
public Manipulation(T obj){
this.obj = obj;
}
public void manipulate(){
// 在这里编译器会报错
obj.f();
}
}
class Manipulation<T extends HasF> {
private T obj;
public Manipulation(T obj){
this.obj = obj;
}
public void manipulate(){
// 由于,编译通过
obj.f();
}
}
// 可以将所有的T替换为HasF,上面例子的泛型没有任何帮助
class Manipulation {
private HasF obj;
public Manipulation(HasF obj){
this.obj = obj;
}
public void manipulate(){
// 由于,编译通过
obj.f();
}
}
// 这种情况下,? extends SuperClass不能使用SuperClass代替
Class<? extends Fruit> fruit = new Apple(); // 可以
Class<Fruit> fruit = Apple.class; //出错
public class Plate<T> {
public T f(T obj) {
// instanceof 在jdk1.8可以用
if (obj instanceof ArrayList) {
// new 报错
T t = new T();
System.out.println(true);
}
// 强转报错
return (Object)obj;
}
}
// 可以new object
this.elementData = new Object[initialCapacity];
// 不可以new E
this.elementData = new Object[initialCapacity];
class Plate<T extends Apple & Fruit & FruitFactory & Generator>
// Apple10必须同时是Apple & Fruit & FruitFactory & Generator几个的子类
Plate<Apple10> plate = new Plate<>();
public class Fruit {
class A{
}
}
public class Apple extends Fruit{
// 不能static class B
class B extends Fruit.A{
}
void f(){
Fruit.A a = new Apple.B();
}
public static void main(String[] args) {
// 创建内部类对象的正确方式
Apple apple = new Apple();
Apple.B b = apple.new B();
}
}
interface Map<K,V> {
interface Entry<K,V> {
}
}
class HashMap<K,V> implements Map<K,V>{
// 这里可以使用static,因为Map.Entry是接口
static class Node<K,V> implements Map.Entry<K,V> {
}
}
interface Iterable<T> {
Iterator<T> iterator();
}
class ArrayList<E> implements ... Iterable<E> {
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
public boolean hasNext() {}
public E next() {
// 要引用外部类当前对象的方式
Object[] elementData = ArrayList.this.elementData;
}
public void remove() {}
}
}
// 1.自己实现Iterator接口
class ArrayList<E> implements ... Iterable<E>,Iterator<T> {
// 模糊了is-a的概念
// 若ArrayList原来就有hasNext、next、remove方法,会产生冲突
public Iterator<E> iterator() {
return this;
}
public boolean hasNext() {}
public E next() {}
public void remove() {}
}
// 2.在外面实现Iterator接口
class Itr implements Iterator<E> {
// 虽然持有了一个ArrayList,但是只能访问arrayList的public方法
// 不能像内部类一个访问其private成员
private ArrayList arrayList;
Itr() {}
public boolean hasNext() {}
public E next() {}
public void remove() {}
}
class ArrayList<E> implements ... Iterable<E> {
public Iterator<E> iterator() {
return new Itr(this);
}
}
public class Outer {
public Fruit fruit(){
// 这个内部类在方法里
class Pear implements Fruit {}
return new Pear();
}
public Fruit fruit(){
// 匿名内部类
return new Fruit(){
// 里面是接口方法的实现
};
}
}
public interface Fruit {
}
public interface FruitFactory {
Fruit getFruit();
}
class Apple implements Fruit{}
class Pear implements Fruit{}
FruitFactory fruitFactory = new FruitFactory(){
@Override
public Fruit getFruit() {
return new Apple();
}
};
Fruit fruit = new FruitFactory() {
@Override
public Fruit getFruit() {
return new Pear();
}
}.getFruit();
接口内部的类自动是public和static
interface Map<K,V> {
interface Entry<K,V> {
}
}
InjectionMetadata buildAutowiringMetadata1(final Class<?> clazz) {
for (Field field : getDeclaredFields(clazz)) {
...
}
}
InjectionMetadata buildAutowiringMetadata2(final Class<?> clazz) {
class MyFieldCallback implements FieldCallback {
void doWith(Field field){
...
}
}
MyFieldCallback myFieldCallback = new MyFieldCallback();
for (Field field : getDeclaredFields(clazz)) {
myFieldCallback.doWith(field);
}
}
InjectionMetadata buildAutowiringMetadata3(final Class<?> clazz) {
ReflectionUtils.doWithLocalFields(targetClass, field -> {
...
});
}
只能有一个方法,不然不知道实现哪个
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
...
// currElements要求必须是final的
currElements.add(new AutowiredFieldElement(field, required));
}
});
static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
for (Field field : getDeclaredFields(clazz)) {
fc.doWith(field);
}
}
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Inter msg = new Inter() {
public String zhuanhuan(Integer p) {
return String.valueOf(p);
}
}
lambda转换结果:
Inter msg =(p)-> return String.valueOf(p);
Consumer c = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
转换结果:
Consumer c = (o) -> {
System.out.println(o);
};
public interface Inter {
public R zhuanhuan(P p);
}
//lambda
Inter msg =(p)->String.valueOf(p);
简化为:
Inter msg = String::valueOf;
interface Inter{
public int compare(P p1,P p2);
}
实现对string的compareto方法
Inter msg = String::compareTo;
System.out.println(msg.compare("A","B")); // -1
功能型:接受一个参数,返回一个结果,例如string.valueof
消费型:接受参数,不返回结果,例如sout
供给型:接受参数,不返回结果,例如string.touppercase
//功能型接口
Function fun = "hello"::startsWith;
System.out.println(fun.apply("he")); //true
//消费型接口
Consumer cons = System.out::println;
cons.accept("hello"); //hello
//供给型接口
Supplier sup = "hello"::toUpperCase;
System.out.println(sup.get()); //HELLO
Consumer f = System.out::println;
Consumer f2 = n -> System.out.println(n + "-F2");
//执行完F后再执行F2的Accept方法
f.andThen(f2).accept("test");
//连续执行F的Accept方法
f.andThen(f).andThen(f).andThen(f).accept("test1");
Plate extends Fruit> p=new Plate(new Apple());
但是,不能set值,只能get值,而且get的值不能赋给Apple,只能是fruit或fruit以上的值。
Plate super Fruit> p=new Plate(new Food());
下界 super T>不影响往里存,但往外取只能放在Object对象里
collection 和 map
数组是一块内存连续的空间
数组数据类型固定了,而集合如果不加泛型可以添加任何数据类型
List:有序,元素不唯一
Set:无序,元素唯一
Queue:队列
linkedlist实现了list接口和queue接口,可以当做栈或队列使用
peek与poll的区别:
peek不移除元素,poll移除
equals
== 比较的是两个对象在对中的地址
equals如果不重写,比较的也是地址
equals是object的方法,不能比较基本类型
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);
输出true
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1 == i2);
输出false
例如:student对象,student 实现 Cloneable接口,重写clone方法
不管shuffle的是origin或sub,都会影反应到对方列表中,所以containsAll会返回true
遍历list时候,我要写一个遍历方法,遍历set的时候我也要写一个遍历方法,我可以让list和set的实现类都实现Iterable接口,自己返回一个iterator,因此对所有类型的容器遍历,我都只需要遍历iterator即可
1.遍历keyset,然后get(key)
2.map.entryset.iterator
ArrayList底层是数组,随机访问比linkedlist快
linkedlist底层是链表,插入和删除较快
hashset:底层使用哈希存储,查找速度快
treeset:底层使用红黑树,可以排序
linkedhashset:使用了链表维护元素的插入顺序
return之后finally总会执行的,finally语句在return语句执行之后,返回之前执行
throwable是exception、error的父类
运行时:ArithmeticException、空指针异常、下标越界异常
编译时:ioException、SQLexception、用户自定义异常
在controller层捕获异常,返回用户提示信息
因为事务已经保证了代码在出现异常后会回滚,所以没有进行其他处理
添加try-catch程序会继续向下执行,否则终止
for (int i = 0; i < 10; i++) {
try {
int j = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("33333");
}
关闭文件,关闭网络连接,释放数据库连接
就近匹配:第一个匹配到后面就不匹配了
派生类异常对象可以匹配基类
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
// 虽然已抛出,但是调用者可以不处理,因为这运行时异常
throw ex;
}
两个线程同时发现instance == null ,那么都会进入创建对象的代码块
当发现为空之后,先持有一个锁(this.class),持有锁之后再次判断instance是否为null,若为null则new instance
一个接口command,里面有两个方法execute,cancel,将ACommand,Bcommand、Ccommand放入一个command的队列中,派出一个线程执行队列中的command的execute方法,若要回退则反向执行command的cancel方法
持有对象与继承类的区别
适配器可以包装多个类,外观模式也可以只包装一个类
外观模式中,本来我需要调用基层类的很多复杂接口,现在只需调用外观类的一个接口,外观类的这个接口隐藏了调用多个基层类的复杂实现
均实现iterator接口,各自实现hasnext与next方法,例如链表的hasnext的方法实现为element.nextNode == null,next方法为element = element.next
菜单项是叶子节点,print方法是打印菜单项的内容,菜单里边包含菜单项,其print方法是迭代里面的菜单项,调用菜单项的print方法
类有三种状态,四个方法
改为:
类包含三个状态对象,三个对象都实现了四个方法,当然,这个类在一个时刻依然只能是一个状态
远程对象的本地代表,本地代表是可以由本地方法调用的对象
远程方法调用(Remote Method Invocation)
模板渲染,jsp,移动端
转发,校验,不复用的业务简单
web层处理简单的不复用的业务
1.预处理返回结果及异常转换
2.缓存方案,中间件的处理
3.manager层是对多个dao层的组合复用