Google Protocol Buffer
685
2022-05-29
第一章,Java开发中通用的方法和准则
public class Person implements Serializable{ //流标识符(Stream Unique Identfier)类的版本定义,可以显示定义可以隐式定义 private static final long serialVersionUID = 55799L; private String name; public Person() { // TODO Auto-generated constructor stub } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Producer { public static void main(String[] args) { //序列化 Person person =new Person(); person.setName("李瑞龙"); SerializationUtils.writeObject(person); System.out.println("序列化成功!!"); } } public class SerializationUtils { private static String FILE_NAME = "C:/LIRUILONG.bin"; /* * 序列化 * */ public static void writeObject(Serializable s) { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME)); oos.writeObject(s); oos.close(); }catch(Exception e){ e.printStackTrace(); } } public SerializationUtils() { // TODO Auto-generated constructor stub } /* * 反序列化 * @return obj */ public static Object readObject() { Object obj = null; try { ObjectInput input = new ObjectInputStream(new FileInputStream(FILE_NAME)); obj = input.readObject(); input.close(); }catch(Exception e) { e.printStackTrace(); } return obj; } } public class Consumer { public static void main(String[] args)throws Exception { //反序列化 Person p = (Person)SerializationUtils.readObject(); System.out.println("name="+p.getName()); } }
当序列化和反序列化的版本不一致时,反序列化会报一个InvalidClassException异常,原因是类版本发生变化,JVM不能把数据流转换为实例对象,JVM通过SerialVersionUID(流标识符),即类的版本定义的,可以显示定义可以隐式定义(编译器自动申明),JVM反序列化时,会比较数据流中的SerialVersionUID与类中的SerialVersionUID是否相同,不相同抛出异常。依靠显示申明,改变一端的Person后可以运行。即显示申明SerialVersionUID可以避免对象不一致。但尽量不要以这种方式向JVM"撒谎"。
(保存在磁盘上的对象文件包括两部分:
1,类文件描述信息:包括类路径,继承关系,访问权限,变量描述,变量访问权限,方法签名,返回值,以及变量 的关联关系类信息。
2,非瞬态(trtansient)和非静态(static)的的实例变量值。
反序列化时final变量在一下情况不会被赋值:通过构造函数赋值,通过方法返回值赋值,final修饰的属性不是基本类型
private void writeObject(ObjectOutputStream out)throws IOException{ //告诉JVM按照默认的规则写入对象,惯例的写法是写在第一句 out.defaultWriteObject(); //写入相应的值 out.writeInt(salary.getBasePay()); } private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException{ //告诉JVM按照默认规则读入对象,也写在第一句 in.defaultReadObject(); //独处相应的值 salary = new Salary(in.readInt(),0); }
在序列化类中增加writeObject和readObject两个方法,使用序列化的独有机制,序列化回调,Java调用ObjectOutputStream类把一个对象转换为流数据时,会通过反射(Reflection)检查被序列化的类是否有writeObject方法,并且检查其是否为私有,无返回值的特性,若有,则会委托该方法进行对象序列化,若没有,则由ObjectOutputStream按照默认规则继续序列化,在反序列化的时候也会检查是否有私有方法readObject。如果有会通过该方法读取属性。
脚本语言可以随时发布而不用重新部署,即脚本语言改变,也能提供不间断的业务服务。
public class main { public main() { // TODO Auto-generated constructor stub } public static void main(String[] args)throws Exception { // TODO Auto-generated method stub //获取JAVacript的执行引擎(engine)。 ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript"); //建立上下文变量 Bindings bind = engine.createBindings(); bind.put("factor", 1); //绑定上下文,作用域为当前引擎范围。 engine.setBindings(bind, ScriptContext.ENGINE_SCOPE); Scanner input = new Scanner(System.in); while(input.hasNextInt()) { int first = input.nextInt(); int sec = input.nextInt(); System.out.println("输入参数为:"+first+","+sec); //执行js代码 engine.eval(new FileReader("C:/model.js")); //是否可调用方法 if(engine instanceof Invocable) { Invocable in = (Invocable)engine; Double result = (Double)in.invokeFunction("formula",first,sec); System.out.println("运行结果:"+result.intValue()); } } } }
public interface Bindings extends Map
慎用动态编译(注意:在框架中谨慎使用,不要在要求高性能的项目中使用,动态编译要考虑安全问题,记录动态编译过程)
避免instanceof非预期的结果。(instanceof操作符的左右必须有继承或实现关系)
"String" instanceof String //返回值为true new String() instanceof String //返回值为true new Object() instanceof String //false(可以编译) 'A' instanceof Character //编译不通过,A为基本类型,Character为封装类,前边必须为对象。 null instanceof String //false,特殊规则,如果左操作数是null,结果就直接返回false,不在运运算右操作数, (String)null instanceof String //false,null是一个万用类型,可以说它没有类型,即使类型转换也是null。 new Date() instanceof String //编译不通过,没有继承实现关系。 T(T为泛型String类变量) instanceof Date; //通过,false,T被编译为Object类,传递String类的值,所以 "Object instanceof Date";
assert
对于final修饰的基本类型和String类型,编译器会认为他是稳定态(Immutable Status),所以编译期间之间把值编译到字节码中,避免运行期引用(Run-time-reference),提高代码执行效率对于final修饰的基本类型和String类型,编译器会认为他是稳定态(Immutable Status),所以编译期间之间把值编译到字节码中,避免运行期引用(Run-time-reference),提高代码执行效率,对于final类来讲编译器认为它是不稳定的,在编译期建立则是引用关系,即到final修饰一个类或实例时,不重新编译也会是最新值。
第二章,基本类型
i%2 == 1?"奇数":"偶数";//输入-1为偶数 public static int remainder(int dividend,int divisor){ return dividend - dividend/divisor*divisor; }
public static Integer valueOf(int){ final int offset= 128; if(i>=-128&&i<=127){ return IntegerCache[i+offset];} return new Ingeger(i); } static final Integer cache[] = new Integer[-(-128)+127+1]; static{ for(int i =0; i < cache.length;i++) cache[i] = new Integer(i-128) }
在java中,随机数的产生取决于种子,随机数和种子之间的关系(种子不同,产出的随机数不同,种子相同,即使实例不同也产生相同的随机数)
public static void main(String[] args) { //Random r= new Random();默认种子 Random r= new Random(1000);//设置种子 for(int i=1;i<4;i++) { System.out.println("随机数字为:"+r.nextInt()); } }
获得随机数:Math.random()方法,通过java.util.Random;
第三章,类对象及方法
静态变量是类加载时被分配到数据区(Data Area)的,它在内存中只有一个拷贝,不会被分配多次,其后的所有赋值操作都是值改变,地址则保持不变。JVM初始化变量是先声明空间,然后在赋值(int i= 12;==>int i ; i =12;)
静态变量在类初始化时首先被加载的,JVM会查找类中所有的静态申明,然后分配空间,只是完成地址空间分配,还没有赋值,之后会根据类中的静态赋值(包括静态类型赋值和静态块赋值)的先后顺序执行。变量先申明后使用。
实例对象有两个类型:表面类型(Apparent Type)和实际类型(Actual Type),表面类型是声明时的类型,实际类型是对象产生时的类型,对于非静态方法,它是根据对象的实际类型来执行的,对于静态方法来说,不依赖实例对象,通过类名访问,通过对象访问静态方法,JVM会通过对象的表面类型查找到静态方法的入口,然后执行,
在子类中构建与父类相同的方法名,输入参数,输出参数,访问权限(权限可以扩大),并且父类子类都是静态方法,此种行为称之为隐藏(Hide),它与覆写有两点不同。
1,表现形式不同:隐藏用于静态方法,覆写用于非静态,@OVerride可以用于覆写(写上自动检测是否合要求),不能用于隐藏。
2,职责不同:隐藏的目的是为了抛弃父类静态方法,重现子类方法,覆写是为了将父类的行为增强或减弱。
public class Base { //父类静态方法 public static void doSomething() { System.out.println("我是父类的静态方法"); } //父类非静态方法 public void doAnything() { System.out.println("我是父类的非静态方法"); } } public class Sub extends Base{ //子类同名,同参数的静态方法 public static void doSomething() { System.out.println("我是子类的静态方法"); } //覆写父类的非静态方法 @Override public void doAnything() { System.out.println("我是子类的非静态方法"); } } public class Client1 { public static void main(String[] args) { Base base1 = new Sub(); Sub base = new Sub(); base.doAnything(); base1.doSomething(); base.doSomething(); } } //结果 //我是子类的非静态方法 //我是父类的静态方法 //我是子类的静态方法
代码块(Code Block):{}包裹的数据体,实现特定算法,一般不能单独运行,要有运行主体,java 中有四种:
1,普通代码块:方法名后面{ }部分。
2,静态代码块:在类中使用static修饰的{ },用于静态变量的初始化和对象创建前的环境初始化。类中的静态块会在整个类加载过程中的初始化阶段执行,而不是在类加载过程中的加载阶段执行。初始化阶段是类加载过程中的最后一个阶段,该阶段就是执行类构造器
3,同步代码块:使用synchronized修饰的{ },表示同一时间只能有一个线程进入到该方法块,一种多线程保护机制。
4,构造代码块:在类中没有人任何前缀和后缀的{ },编译器会把构造代码块插入到构造函数的最前端。
在通过new关键字生成一个实例时会先执行构造代码块,然后在执行其他构造函数代码,依托于构造函数运行,不是在构造函数之前运行。应用:
1,初始化实例变量(Instance Variable):如果每个构造函数都需要初始化变量,可以通过构造代码块实现。
2,初始化实例环境:当一个对象必须在适的场景才能存在,jee中要产生HTTP Request,必须要建立HTTP session ,可以在创建HTTP Request时通过构造代码块检查HTTP Session是否存在,不存在就创建。
java中的嵌套类(Nesetd Class):分为两种,静态内部类(也叫静态嵌套类,Static Nested Class)和内部类(Inner Class),
静态内部类:加强了类的封装性,提高了代码的可读性。
静态内部类不持有外部类的引用(普通内部类可以访问外部类的方法,属性,即使是private类型也可以访问,静态内部类只可以访问外部类的静态方法和静态属性),静态内部类不依赖外部类(普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例,同生同死,一起声明,一起被拉圾回收器回收,静态内部类可以独立存在,即使外部类消亡,静态内部类还是可以存在),普通内部不能声明static的方法和变量(常量可以修饰,静态内部类没有限制)
//声明一个ArrayList对象。 List la = new ArrayList(); //一个继承了ArrayList的匿名类的声明和赋值,没有任何覆写方法。 List lb = new ArrayList(){}; //在上面的基础上增加了构造函数块。可以有多个。 List lc = new ArrayList(){{}{}};
enum Ops{ADD,SUB } public class Calculator { private int i,j,result; public Calculator() { } public Calculator(int i,int j) { this.i =i; this.j = j; } protected void setOperator(Ops _op) { result = _op.equals(Ops.ADD)?i+j:i-j; } public int getResult() { return result; } public static void main(String[] args) { //定义一个匿名内部类,使用构造代码块初始化 Calculator c1 = new Calculator(1,2) { { setOperator(Ops.ADD); } }; System.out.println(c1.getResult()); } }
//父亲 public interface Father { public int strong(); } //母亲 public interface Mother { public int Kind(); } //父亲实现类 public class FatherImpl implements Father { @Override public int strong() { return 8; } } //母亲实现类 public class MotherImpl implements Mother{ @Override public int Kind() { return 8; } } //儿子 public class Son extends FatherImpl implements Mother{ @Override public int strong() { return super.strong()+1; } @Override public int Kind() { return new MotherSpecial().Kind(); } public class MotherSpecial extends MotherImpl { public int kind() { return super.Kind() -1; } } }
public class UtilsClass{ private UtilsClass(){ throw new Error("不要实例化我哎"); } }
一个类在实现了Cloneable接口就表示它具备了被拷贝的能力,如果在覆写clone()方法就会完全具备拷贝能力。拷贝在内存中进行,所以在性能方面比直接通过new生成对象要快的多,存在缺陷:浅拷贝(Shadow Clobe,也称影子拷贝)存在对象属性拷贝不彻底的问题。拷贝规则:
1,基本类型拷贝其值,。
2,实例对象,拷贝地址引用,就是说此时新拷贝的对象与原有对象共享该实例变量,不受访问权限的限制。
3,String字符串,拷贝的也是地址,但是在修改时,会从字符串池(String Pool)中重新生成字符串,原有的字符串保持不变
public class Person implements Cloneable{ //姓名 private String name; //父亲 private Person father; public Person(String name) { this.name = name; } public Person(String name,Person parent) { this.name = name; this.father = parent; } //getter与setter方法省略 public Person clone() { Person p = null; try { p = (Person)super.clone(); //p.setFather(new Person(p.getFather().getName())实现深拷贝 }catch(CloneNotSupportedException e) { e.printStackTrace(); } return p; } } public class Client2 { public static void main(String[] args) { Person f = new Person("父亲"); Person s1 = new Person("大儿子",f); Person s2 = s1.clone(); s2.setName("小儿子"); //s2.getFather().setName("干爹"); System.out.println(s1.getName()+"的父亲为:"+s1.getFather().getName()); System.out.println(s2.getName()+"的父亲为:"+s2.getFather().getName()); } }
被拷贝的类只要实现Serializable接口,不需要任何实现,需要加上SerialVersionUID常量,使用需要注意:
1,对象的内部属性都是可序列化的。
2,注意方法和属性的特殊修饰符。final,static变量的序列化问题会被 引入到拷贝对象中,瞬态变量(trtansient)不能进行序列化。可一采用Apache下的commons工具包中的SerializationUtils类。
public class CloneUtils { //拷贝一个对象 //@SuppressWarnings。该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。 /* * 关键字 用途 deprecation 使用了不赞成使用的类或方法时的警告 unchecked 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。 fallthrough 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。 path 在类路径、源文件路径等中有不存在的路径时的警告。 serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告。 finally 任何 finally 子句不能正常完成时的警告。 all 关于以上所有情况的警告。 @SuppressWarnings 批注允许您选择性地取消特定代码段(即,类或方法)中的警告。其中的想法是当您看到警告时, 您将调查它,如果您确定它不是问题,您就可以添加一个 @SuppressWarnings 批注,以使您不会再看到警告。 虽然它听起来似乎会屏蔽潜在的错误,但实际上它将提高代码安全性,因为它将防止您对警告无动于衷 — 您看到的每一个警告都将值得注意。 */ @SuppressWarnings("unchecked") public static
HashMap的底层处理机制是以数组的方式保存Map条目的,链表保存val,依据传入元素的hashCode方法返回的哈希值决定数组下标,如果该位置已有Map条目了,且与传入的键值相等则不要处理,若不相等则则覆盖,如果数组位置没有条目则插入。并加入到Map条目的链表中。即在检查相等时也是由哈希吗确定位置。
哈希码:由Object方法本地生成,确保每一个对象有一个哈希码(哈希算法,输入任意L,通过一定算法f(L),将其转化为非可逆的输出,一对一,多对一成立),重写hashCode方法:
public int hashCode(){ return new HashCodeBuilder().append(),toHashCode();//HashCodeBuilder哈希码生成工具。 }
Java JVM
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。