Java中的8种基本数据类型:
整型:byte (1字节),short(2字节),int(4字节),long(8字节)
浮点型:float(4字节),double(8字节)
字符型:char
布尔型:boolean
注意:
浮点数值不适用于禁止出现舍入误差的金融计算中!
如果需要在数值计算中不含有任何舍入误差,就应该使用BigDecimal类
常量
关键字final表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了!
如果希望某个常量可以在一个类中的多个方法中使用,则声明为静态的。
(public) static final int PI = 3.1415926;
位运算
处理整型数值时,可以直接对组成整型数值的各个位进行操作。这意味着可以使用屏蔽
技术获得整数中的各个位。
& (与)
| (或)
^ (异或)
~ (非)
对于int类型,移位运算符右侧的参数需要进行模32的运算(int类型总共就32位)
1 << 35,1 << 3,计算结果都等于 8
对于long类型,需对右侧操作数模64 (long类型64位)
Math函数
求1个数的次幂,如x的a次幂:double y = Math.pow(x,a); [pow接收的参数为double类型]
比较大小,Math.max(), Math.min()
枚举类型
变量的取值只在一个有限的集合内。
枚举可以定义在类中(非public修饰枚举类)
也可以独立进行定义(public修饰时必须单独定义)。
package javacore; public enum Size { SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL"); private String abbreviation; private Size(String abbreviation) { this.abbreviation = abbreviation; } public String getAbbreviation() { return abbreviation; } }
字符串
字符串是不可变类型
每个用双引号括起来的字符串都是String类的一个实例。
每次连接字符串,都会构建一个新的String对象,既耗时,又浪费空间。
当字符串拼接量大时,请使用StringBuilder,而不要直接用+号进行拼接
字符串不可变的优点:编译器会共享相同的字符串,在内存中只有一份。
如果所有字符串都在一个单线程中编辑(通常都是这样),则应该用StringBuilder替代StringBuffer。
比较字符串是否相等:
请使用equals()进行比较,不要使用==比较
原因:
String s1 = "abc"; String s2 = new String("abc"); System.out.println(s1==s2);//false System.out.println(s1.equals(s2));//true
== 是直接比较内存地址
equals() 先比较内存地址,如果不同,会逐个比较字符串中的字符
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; }
启动路径
启动路径就是命令解释器的当前路径
String dir = System.getProperty("user.dir"); System.out.println(dir);
输出:E:\project\NIO
代码块
代码块中的代码将在构造函数被调用时执行,执行顺序:
1.本来构造方法中,先通过super()调用父类构造方法
2.执行代码块中的语句
3.执行构造函数中未被执行的语句
switch语句
只能处理整型和枚举类型,另外需要小心case穿透!
BigInteger与BigDecimal
这两个类可以处理包含任意长度数字序列的数值。
BigInteger类实现了任意精度的整数运算;
BigDecimal实现了任意精度的浮点数运算,算钱的时候一定要用它。
类
类是实例对象的封装体,包含对象的属性和行为
类中的数据/字段field,称为实例域
每个特定的类实例都有一组特定的实例域值,这些值的集合就构成了当前对象的状态
类中的方法method,称为行为/功能
通过方法可以改变对象的状态,通过这些变化的状态再次去影响其它方法的执行
方法需要结合当前对象的状态去执行某种行为,称为有状态的方法
如果方法的执行不依赖对象的状态,那么该方法是无状态的,直接定义为静态即可!
类不应该直接将实例域开放给外界,而是提供访问的方法,让本类对象在外部进行调用。
设计一个类的时候,应该考虑:
对象是否具有状态(state) : 如有,那应该存在哪些状态(属性)
对象的行为(behavior):需要具备哪些行为?这些行为是否与对象的状态有关?是否需要考虑多线程访问的安全性(同步)?
同一个类的不同实例都具备相同的行为,但是由于各个对象之间的状态互不相同,导致不同对象的行为结果也不相同。因此,对象的状态影响着对象的行为!!!
良好的类设计原则:
1. 一定将数据私有化
2. 一定对数据进行显示初始化,不要使用其默认值
3. 不要在类中使用过多基本数据类型,如String province,String city可以抽取到Address类
4. 将职责过多的类进行分解
5. 类名和方法名要见名知意
6. 正确的使用静态属性与静态方法
类与类之间建立关系
依赖 use-a :
---------------->
体现为局部变量、方法的参数或者对静态方法的调用,如sellTick()依赖IDCard类
关联 has-a:
——————>
通过使用成员变量来实现关联,如人与身份证是关联关系。
聚合 has-a:
空心菱形——————>
是关联关系的一种,是强的关联关系,如汽车与轮胎是聚合关系。
组合 has-a:
实心菱形——————>
代表整体的对象负责代表部分的对象的生命周期,如人与心脏就是组合关系。
继承 is-a:
———————空心箭头
侧重于对父类功能的复用
实现 is-a:
----------------------空心箭头
侧重于扩展子类的功能(面向接口编程,接着利用多态特性,动态绑定子类。。。)
对象
对象就是所属类的一个实例。
OOP编程,其实就是创建各种对象,然后通过这些对象的行为去协作完成系列功能
对象的初始化过程
1.对所有数据域进行初始化(0,false,null);
2.按照类中的声明次序,依次执行所有域的初始化语句和代码块;
3.如果构造器第一行调用了其它构造器,则执行其它构造器的主体;
4.最后,才执行本构造器的主体。
一个对象变量并没有实际包含一个对象,而仅仅引用该对象在堆内存中的地址。
Java中的参数传递方式只有一种:值传递!
1.参数是普通数据类型,传递的就是值本身;
2.参数是引用类型,传递的就是对象在堆中的地址。
public class Sample { { this.name = "fds"; } private String name; public Sample(String name) { super(); this.name = name; } private static void swap(Sample x, Sample y) { //这里对两个局部变量的值进行了交换,对地址所引用到的堆内存进行了改变,改变是有效的 Sample temp = x; x = y; y = temp; x.name = "xxx"; y.name = "yyy"; } public static void main(String[] args) throws CloneNotSupportedException { Sample x = new Sample("x"); Sample y = new Sample("y"); //仅仅是将x,y的地址值作为了参数进行传递 swap(x,y); //x变量仍然指向原来的堆内存 System.out.println(x.name); //输出:yyy //y变量仍然指向原来的堆内存 System.out.println(y.name);//输出:xxx } }
实例域(对象属性)的初始化方式
1. 定义的时候进行赋值
2. 构造函数中进行赋值
3. 代码块中进行赋值(一旦构造函数被调用,代码块就会被执行,且优先于构造函数中的赋值操作)
静态域(类变量)的初始化
如果静态域的初始化比较复杂,可以通过静态代码块解决!
设计实例域方法的相关原则:
1.私有化
2.提供共有的域访问方法,该方法可以隐藏一些内部细节,对外直接返回结果即可
3.提供共有的域更改方法,方法内部控制是否允许修改,以及如何修改,提高灵活性
注意:
不要编写返回引用可变对象的访问器方法。如返回Date引用,在外部可直接修改其值!
如,下面的做法非常危险!
Date retireDate = new Date(2010-1900,1,1); public Date getRetireDate() { return retireDate;//Do not do this! //return (Date) retireDate.clone(); } public void someMethod() { Date newDate = getRetireDate(); System.out.println(newDate); newDate.setTime(System.currentTimeMillis()); System.out.println(getRetireDate()); }
clone(): 返回一个可变数据域的拷贝。
对象行为的设计原则:
公有行为/功能需要开放为public权限;
对行为提供内部支持的方法设置为private权限,不需要暴露给外界
静态static
static修饰的实例域或者方法,属于类级别的,与对象无关。
对于static的应用,一般有一下几种:
1. 静态字段,一般用来设置公用常量,并且用final声明其不可变性。
典型用法:public static final int PI = 3.1415926
2. 静态方法。什么时候适合将方法声明为静态的呢?
当一个方法不需要访问对象的状态,其所需参数都是通过显示参数提供的,如Math.pow()
当一个方法只需要访问类的静态域
静态方法的灵活应用:演变为Facory方法
通过静态方法返回类的实例,为什么这样做?直接通过构造函数new对象不行吗?
1. 可以给方法起一个更直观的名字,让调用者一看便知道将返回一个什么对象
2.通过方法返回对象,方法内部可以做各种控制,甚至返回一个其它对象,或抛出异常
什么时候适合用静态导入?
1. 调用Math函数的方法时,静态导入Math类,然后直接使用max(),min等方法;
2. 如果静态域所在类名很长,可以考虑将其静态导入;
其它情况则不推荐,因为静态导入会降低程序的可读性。
继承
如果类之间存在 is-kind -a 关系,则可用继承来设计。
继承设计原则:
在设计类的时候,将通用的方法定义在父类中,具有特殊用途的方法则定义在子类中。
使用继承可以实现什么?
继承最直观的作用就是复用父类已经存在的非私有的域和方法。
子类在此基础上再扩充新的域和方法,提供更多的功能。
一种为纯复用式,子类继承父类的域和方法,如Manager extends Employee;
另一种则更灵活,父类既提供可复用的域和方法给子类,又以多态的方式参与到程序中。
比如,模板方法设计模式中:
父类定义1个统一操作的方法,该方法中会调用到另外几个抽象方法。
子类按各自需求复写这些抽象方法。
在运行时调用模板方法,父类引用将动态绑定到子类对象上,最终执行子类复写的方法。
程序中,任何出现父类的地方,都可以用子类进行替换!
因此,如果考虑将类设计为继承关系,请明确这样做的目的是什么? 复用 / 多态?
实际开发中,父类是根据一系列子类向上抽取得到的!
至于抽取哪些域和方法放到父类中,需要根据实际所处环境而定。
子类中如何操作父类中的属性
子类如果要操作父类中的私有属性,有两种途径:
1. 在子类构造方法中,通过super()调用父类构造方法。如,super(name)。
然后子类中通过getName()便可获取到name值;
实际例子:
Thread子类设置线程名称时,就是通过super(name)去调用Thread类的构造方法,设置Thread类中的私有name属性(private char[] name ),然后子类再使用getName()方法获取到所设置的线程名称。
super.getName() 明确指定访问的getName()是父类的,而不是子类的。
当在子类从父类继承下来的getName()方法中,需要获取父类私有属性name时,只能通过父类对外开放的getName()。如果在子类的getName()中直接调用getName(),将发生无限循环,此时,必须通过super.getName()明确的告诉编译器访问的是父类的getName()。
2.将父类中私有属性权限提高到protected权限,子类便可直接操作这些属性。
子类覆盖父类中的方法,可见性必须大于父类中对于方法的可见性
final的用法
将final声明在类上,可以阻止该类被继承,类中的方法自然也限定为final的,而不包括域;
可以单独限制类中的方法为final,但类中的其它方法仍可以被子类覆盖;
可以单独限制final域,构造对象之后就不可以改变该域的值。
父类与子类之间的转换
向上转型
子类可以直接转换为父类(多态:父类引用具体子类对象)
向下转型
将超类转换为子类之前,应该使用instanceof进行检查,防止ClassCastException
抽象类
如果基类只作为派生其它类而存在,而不需要作为实例类,则可以将其设计为抽象类
包含1个或多个抽象方法的类,必须声明为抽象类
除抽象方法外,抽象类可以包含具体数据域和方法
抽象类中可以没有抽象方法,可以阻止该类被实例化
抽象类中,可以有一部分抽象方法,一部分具体方法。如模板设计模式
抽象类的变量,可以引用非抽象子类的实例。如模板设计模式
访问权限控制
private 仅在本类中可见
package 仅在本包可见
protected 在本包和子类中可见
public 对所有类可见
权限设计的建议:
1. 属性都设为私有的。当有继承关系存在:
为了方便子类直接使用父类中的域,可在父类中将这些域设置为protected权限;
2. 如果方法只在本类使用,则设置为private权限;
3. 如果需要一定程度的限制某个方法的调用,则设置为protected权限;
4. 如果方法是公用的,则设置为public权限;
Object类中的方法
equals(Object otherObj)
Object类中的equals()比较的是两个对象的内存地址是否相同;
比较对象是否相同,参与比较的双方应该都是同一个类型才有意义。
如何定义2个对象是否相同,需要从对象"相等"的语义出发:
如,只要属性都相同,就是同一个对象;或者只要一部分属性相同,就认为同一个对象。
所以,需要根据具体情况去决定equals()的比较逻辑。
在比较对象是否相同时,首先需要判断对象是否同属于一类的范畴。
如果子类拥有自己的相等概念,则对称性需求将强制采用getClass()进行检测;
如果由超类决定相等概念,则可以使用instanceof进行检测,这样可以在不同子类之间进行相等的比较;
如果子类都使用同一个的语义比较对象是否相等,则使用instanceof;
if(!(otherObject instanceof ClassName)) return false;
如果equals()的语义在不同子类之间有所改变,就需要使用getClass(),防止失去对称性;
if(this.getClass()!==otherObject.getClass()) return false;
标准的equals()书写范例:
Parent.class
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; //if(!(obj instanceof Parent)) if (getClass() != obj.getClass()) return false; Parent other = (Parent) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
Child.class
@Override public boolean equals(Object obj) { //先调用父类的equals进行比较 if(!(super.equals(obj))) return false; Child other = (Child) obj; if (bounds != other.bounds) return false; return true; }
hashCode()
计算对象的散列码。
当使用哈希表数据结构存放数据时,就需要调用此方法计算对象的哈希码值。
当哈希码值相同时,需要调用对象的equals()比较对象是否相同,如果相同,则丢弃。
只有同时将equals和hashCode进行复写,才能保证使用基于哈希表结构的集合操作不出错!
如,HashSet基于hashCode()和equals()可以实现集合中元素的唯一性;
HashMap同样基于hashCode()和equals(),实现Map集合中的Key唯一;
toString()
返回对象值的字符串表现形式
建议为所有自定义的类增加toString()
复写toString()的技巧:
父类:使用getClass.getName(),而不是具体的父类名
子类:调用super.toString()+子类特有的描述
Parent.class
@Override public String toString() { return getClass().getName()+"[name=" + name + ", age=" + age + "]"; }
Child.class
@Override public String toString() { return super.toString()+",[bounds=" + bounds + "]"; }
getClass()
返回包含对象信息的类Class
Java提供的类运行时的描述,内容被封装在Class类中
基于字符串创建对象。默认调用无参构造方法,所以要确保该类有无参构造方法存在。
Class clazz = Class.forName("xxxx");
clazz.newInstance();
如果需要调用带参数的构造方法,则使用:
clazz.getConstructor(Class...paramTypes).newInstance(Object...params);
clone()
创建一个对象的副本,系统将为新复制的对象在堆内存中分配新的存储空间。
克隆对象的时候要注意浅复制与深复制的不同。
被复制的对象,必须实现Cloneable接口才能被复制。
ArrayList
可变数组,底层仍通过数组进行数据的存储,只是将功能封装得更便于操作。
当容量不够时,ArrayList会自动创建更大容量的数组,并将原数组中的元素复制到新数组中。
对于增删操作频繁的情况,会导致频繁移动数组中的其它元素,这种情况下应该使用基于链表结构的LinkedList。
ArrayList<T>通过泛型限定,使集合操作更加安全,避免了类型转换导致的问题
ensureCapacity(minCapacity)
需要保证ArrayList至少容纳多少元素
trimToSize()
将数组列表的存储容量削减到当前尺寸
ArrayList<Integer>与int[ ]之间效率的比较
ArrayList<Integer>效率远远低于int[ ]的效率,因为涉及到基本数据类型值的装箱与解包。
当数组元素少的时候,方便性比执行效率更重要,使用ArrayList<Integer>;
当数组元素多的时候,可能就需要考虑使用底层的int[ ]了;
装箱:list.add(3) ---> list.add(new Integer(3));
解包:list.get(i).intValue()
装箱与解包由编译器自动完成,程序员只需要知道有这样一个过程,以及其对效率的影响即可。
NumberFormat的使用
NumberFormat nf1 = NumberFormat.getPercentInstance(); nf1.setMaximumFractionDigits(2); String percent = nf1.format(0.23456); System.out.println(percent);//23.46% NumberFormat nf2 = NumberFormat.getCurrencyInstance(Locale.CHINA); nf2.setMinimumFractionDigits(4); nf2.setMinimumIntegerDigits(4); String currency = nf2.format(122.34567); System.out.println(currency);//¥0,122.3457
可变参数
//参数采用数组接收 public void print1(String param, Object[] objects) { } //Java5.0新增特性:可变参数 public void print2(String param, Object...objects) { }
反射
在运行时分析类
在运行时查看对象
在运行时动态调用方法
Class类:在程序运行期,Java为每个对象都维护一个运行时的标识, 保存对象的状态
Class.forName(Stirng className)
根据字符串生成对应类的Class对象
newInstance()
通过默认构造方法创建对象
newInstance(Object[] args)
通过带参数的构造方法创建对象
printStackTrace()
将Throwable对象和栈的轨迹输出到标准错误流
获取Class的三种方式:
1.通过对象获取:
Person p = new Person(); Class clazz = p.getClass();
2.通过Class.forName(className)获取:
Class clazz = Class.forName("com.gc.Client");
3.直接通过类获取:
Class clazz = Client.class;
通过反射分析类,灵活访问类中的域和调用类中的方法
三个类:Field、Method和Constructor,分别用于描述类的域、方法和构造器。
Field类有一个getType方法,用来返回描述域所属类型的Class对象
Method和Constructor类有能够报告参数类型的方法:getParameterTypes()
Method类还有一个可以报告返回类型的方法:getReturnType()
这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。
Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是否是public、private或final。
Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。
Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。
setAccessible(boolean flag)
flag为true表明屏蔽Java语言的访问检查,使得对象的私有属性也可以被查询和设置。
注意:一个Class对象实际表示的是一种类型,而此类型不一定是类。
如int不是类,但int.class是一个Class类对象。
基本数据类型的数组类型int[]可以被转换成Object,但不能转换成对象数组。
Object arr = new int[100]; //ok
Object[] arr = new int[100]; //error
getComponentType() 返回数组中元素的类型,如果操作对象不是数组,则返回null
Object objArr = new Child[10]; Class elementType = objArr.getClass().getComponentType(); System.out.println(elementType);//class javacore.Child
利用反射编写泛型数组拷贝
public static void main(String[] args) { int[] arr = {1,2,3,4,5}; Object newArrObj = autoGrow(arr); int[] newArr = (int[])newArrObj; System.out.println(Arrays.toString(newArr));//[1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0] } public static Object autoGrow(Object arr) { Class<?> clazz = arr.getClass(); if(!clazz.isArray()) { return null; } Class<?> componentType = clazz.getComponentType(); int length = Array.getLength(arr); int newLength = length*10/11+10;//扩容 Object newArray = Array.newInstance(componentType, newLength); System.arraycopy(arr, 0, newArray, 0, length); return newArray; }
回调方法(通过反射完成)
Java中没有方法指针的概念,但是可以通过反射进行方法的动态调用。
public Object invoke(Object implicitParameter,Object[] explicitParamenters)
1.循环Method[],找到需要被调用的方法,执行invoke()
public static Object methodInvoke(Object obj, Object...params) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method[] methods = obj.getClass().getDeclaredMethods(); for(Method m : methods) { if(m.getName().startsWith("prefix")) return m.invoke(obj, params); } return null; }
2.直接根据方法名进行获取,需额外指定方法的参数类型(如果有),以便匹配到正确的方法签名
public static Object methodInvoke(Object obj) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ Method m = obj.getClass().getMethod("methodName");//如果方法被重载了,需要额外指定方法参数类型 return m.invoke(obj);//如果有参数,需要传入参数值 }
使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些
建议仅在必要的时候才使用Method对象进行方法调用
可替代解决方案:接口、内部类
反射实现回调方法的另一种更好替代方案:接口
继承的设计技巧:
1.共有域和方法放在超类
2.不要使用protected权限修饰域
3.如果是is-a关系,则继承
4.除非所有继承得到的方法对子类都有意义,否则不要使用继承
5.覆盖方法时,不要改变原本的预期行为
6.充分利用多态性,而非直接面向子类编程
7.不要过多使用反射,虽然程序更通用,但出错几率也随之增加
8.尽量少用继承,多用组合
接口
如果类遵从某个特定接口,那么就履行这项服务。
使用接口的最终目的:为运行时动态绑定提供支持(多态性)
面向接口设计,是一种从全局出发的程序设计思想:
接口中定义一个/组抽象行为,外部提供一个面向接口的具体编程逻辑。
A)编译阶段:面向接口编程,编译器检查接口中确实声明了这些方法,因此,编译不会报错;
B)运行阶段:由于每个实现了此接口的子类一定存在那些通过接口进行调用的方法,因此,动态绑定到具体子类之后调用这些方法也是一定存在的。
接口中只允许出现2种成员:
1. 常量: 接口中定义的域默认就是public static final的;
2. 抽象方法: 接口中定义的方法默认就是public abstract的;
因此,在定义接口时,没必要书写这些多余的关键字,因为它们都是默认并且强制的!
如Comparable接口,描述了对象之间相互比较的一种能力。
具体比较逻辑由子类来实现,外部则封装对compareTo()进行调用的逻辑即可。
克隆
拷贝与克隆的区别
拷贝:当拷贝一个变量时,改变一个变量所引用的对象将同步对另一个对象产品影响
克隆:创建一个新对象,且新对象的状态与原始对象完全相同。当对克隆对象进行改变时,不会对原始对象造成影响。
要想让一个类的实例对象具备克隆能力,必须让该类实现Cloneable接口,该接口是一个标记接口。
clone()是从Object类继承下来的。
默认提供的是"浅拷贝",并不会克隆对象的内部对象。
克隆-深复制:
被克隆的对象内部如果包含其它引用,则需要进一步克隆,否则对克隆对象的子对象改变会同步到原来对象的子对象上。
不推荐使用clone(),所以了解即可!
接口回调 callback
某个特定事件发生时应该采取的动作,监听器Listener应该就是这样做的。
1.首先将子类对象以接口的类型注册到某个地方;
2.当某个事件发生时,马上调用一个notice方法,在方法内部拿到已注册对象,让这些对象分别去执行自己实现的那部分具体行为;
方法的回调仍然通过所属对象进行调用,只是调用时机由某个事件何时发生来决定!
内部类
内部类技术主要用于设计具有相互协作关系的类集合。
内部类的对象默认持有一个外部类的引用,通过该隐式的引用让内部类可以直接访问外部类的所有状态(域和方法)。
1.简单内部类,可以直接访问外部类的实例域
(内部类需要的数据是通过外部类构造函数传入的)
package javacore; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import javax.swing.JOptionPane; import javax.swing.Timer; public class InnerClassTest { public static void main(String[] args) { TalkingClock clock = new TalkingClock(1000, true); clock.start(); JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } } class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval, boolean beep) { this.interval = interval; this.beep = beep; } public void start() { ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start(); } /** * TimePrinter实现了ActionListener接口 * 由于需要使用到beep变量,将其设置为内部类非常合适 */ public class TimePrinter implements ActionListener { @Override public void actionPerformed(ActionEvent e) { Date now = new Date(); System.out.println("Now: " + now); //直接访问外部类的成员 if(beep) java.awt.Toolkit.getDefaultToolkit().beep(); } } }
2.内部类转化为外部类
内部类正式为了方便访问外部类的实例域,才将其作为内部类进行定义。
当然,也可以将内部类转化为外部类,这种情况下访问“外部类”的实例域比较麻烦。
3.局部内部类
(内部类访问的数据是方法中的局部变量)
局部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类
的块中。
这些被内部类使用到的局部变量必须限制为final的。因为局部变量会随着方法结束一起出栈,为了让内部类继续使用这些局部变量,需要通过final进行声明(实际会将局部变量拷贝出一份数据进行存储,供内部类使用)
package javacore; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import javax.swing.JOptionPane; import javax.swing.Timer; public class InnerClassTest { public static void main(String[] args) { TalkingClock clock = new TalkingClock(); clock.start(1000, true); JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } } class TalkingClock { public TalkingClock() {} public void start(int interval, final boolean beep) { /** * 局部内部类 */ class TimePrinter implements ActionListener { @Override public void actionPerformed(ActionEvent e) { Date now = new Date(); System.out.println("Now: " + now); //被访问的局部变量必须声明为final的 if(beep) java.awt.Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start(); } }
扩展:
final关键字可以应用于局部变量、实例变量和静态变量。
在定义final变量的时候,不必进行初始化。
在创建这个变量之后,只能够为之赋值一次。此后,再也不能修改它的值了,这就是final。
在内部类改变final变量的值:使用一个长度为1的数组
public static void main(String[] args) { final int[] count = new int[1]; Date[] dates = new Date[100]; for(int i=0;i<dates.length;i++) { dates[i] = new Date() { @Override public int compareTo(Date anotherDate) { //局部final变量也可以修改了 count[0]++; return super.compareTo(anotherDate); } }; } Arrays.sort(dates); System.out.println("共比较次数:"+count[0]); }
4.匿名内部类
如果只使用1次,可以考虑匿名内部类的写法。
如开启新线程传入Runnable实例:
new Runnable() { public void run() {
}}
5.静态内部类嵌套在辅助类中
当内部类不需要持有外部类的引用时,就可以定义为静态的,以便取消产生的outer引用。
只有内部类才可以修饰为静态的,普通类不可以被static修饰。
代理 Proxy
使用JDK动态代理,需要注意以下几点:
1.被代理类必须实现某个接口。没有实现接口的类无法使用JDK的动态代理!
原因:运行阶段生成的代理类会实现这些接口,然后返回生成的代理对象。这样,在外部通过接口进行引用才能保证代理对象能转换为接口类型;
动态代理底层细节:利用Java语言多态性实现。
A) 通过Interface接口类型去引用返回的代理对象;
B) 当通过接口引用调用方法时,此时的对象动态绑定到代理对象上,然后执行代理对象上的方法(代理对象实现了此接口,因此其内部具有接口中的方法);
C) 当代理对象中的方法被调用时,会自动调用InvocationHandler实现类中的invoke();
D) 在invoke()内部,再利用反射:method.invoke(target,args)去调用真正的对象;
E) 在method.invoke(target,args)前后,可以加入另外的辅助逻辑,这正是我们需要的;
F) 最终,真正的对象上的方法被调用执行了。
2.传入的目标对象是接口的具体实现类,target = new UserServiceImpl();
3.通过代理类Proxy创建代理对象时,传入newProxyInstance()的loader和interfaces应该通过target对象获取。如果传入其它对象的loader和interface会造成生成的代理对象无法使用。
动态代理使用示例:
首先需要1个接口和一个实现类
然后,创建一个封装好的用于生成动态代理的工具类
最后,将返回的代理对象转换为接口类型,调用接口中的方法
package javacore; public interface IService { String doSomething(Object...args); }
package javacore; import org.apache.catalina.tribes.util.Arrays; public class ServiceImpl implements IService { @Override public String doSomething(Object... args) { System.out.println(this.getClass().getName() + "--->doSomething() is running"); return Arrays.toString(args); } }
package javacore; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxyHelper { public static JDKProxyHelper getHelper() { return new JDKProxyHelper(); } private Object target; public Object newProxyInstance(Object target) { //真正的对象 this.target = target; //代理对象 Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InnerProxyHandler()); //返回代理对象 return proxy; } //内部类直接访问外部类的实例域 private class InnerProxyHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object retVal = null; System.out.println("method invoke before: "+method.getName()); try { retVal = method.invoke(target, args); } catch(RuntimeException e) { System.out.println("error occur in method: "+method.getName()); throw e; } System.out.println("method invoke after: "+method.getName()); return retVal; } } }
package javacore; public class ProxyTester { public static void main(String[] args) { ServiceImpl target = new ServiceImpl(); IService service = (IService)JDKProxyHelper.getHelper().newProxyInstance(target); String retVal = service.doSomething("Hello proxy", 100); System.out.println("return:" + retVal); } }
另一个例子:
追踪compareTo()的调用轨迹
首先将用1~1000整数的代理填充数组,然后调用Arrays类中的binarySearch方法在数组中查找一个随机整数。最后,打印出与之匹配的元素。
package javacore; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Random; public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[1000]; for(int i=0;i<elements.length;i++) { Integer value = i+1; InvocationHandler h = new TraceHandler(value); Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, h); elements[i] = proxy; } Integer key = new Random().nextInt(elements.length)+1; int result = Arrays.binarySearch(elements, key); if(result>=0) { System.out.println(elements[result]); } } } class TraceHandler implements InvocationHandler { private Object target; public TraceHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print(target); System.out.print("."+method.getName()+"("); if(args!=null) { for(int i=0;i<args.length;i++) { System.out.print(args[i]); if(i<args.length-1) System.out.print(", "); } } System.out.println(")"); return method.invoke(target, args); } }
异常处理
Java中,所有异常类都继承自Throwable类
Throwable
Error(系统级错误)
Exception
IOException(已检查异常)
RuntimeException(运行时异常)
1. 早抛出,晚捕获;
2. 传递异常比捕获异常更好,让高层次的方法通知用户发生了错误;
3. 当遇到子类方法不能抛出checkedException的时候,可以将其转换为RuntimeException抛给调用者,这样就不需在方法上声明了;
4. 自定义异常,更直观的描述异常;
5. 不要只抛出RuntimeException异常。应该寻找更加适当的子类或创建自己的异常类。
自定义异常类,覆盖Throwable中的构造方法
package javacore.exception; public class AppException extends RuntimeException { /** * 用给定的“诱饵”构造一个RuntimeException对象。 * @param message 异常的简单描述 * @param cause 引发此异常的诱饵 */ public AppException(String message, Throwable cause) { super(message, cause); } public AppException(Throwable cause) { super(cause); } }
package javacore.exception; import java.sql.SQLException; public class Dao { public void dao() throws SQLException { System.out.println("this is dao"); throw new SQLException("Connection timeout"); } }
package javacore.exception; import java.sql.SQLException; public class Service { public void service() { try { new Dao().dao(); System.out.println("this is service"); } catch (SQLException e) { //自定义异常,设置异常消息和诱饵 throw new AppException("error occur in dao",e); } } }
package javacore.exception; public class Action { public static void main(String[] args) { try { new Service().service(); } catch (AppException e) { e.printStackTrace(); // System.out.println(e.getMessage()); // System.out.println(e.getCause().getMessage()); } } }
相关推荐
Tripple Farm:Match 3 Combination Game Complete Project 合成小镇三消Unity合成消除游戏项目游戏插件模版C# 支持Unity2020.3.4或更高 您知道像三合镇这样的著名益智游戏,并且您想制作一个自己的游戏。就是这样。这个包正好适合您。 这是一个完整的项目,您可以在零分钟内将其上传到 appstore 或 googleplay 商店。 基本规则: 3个或以上相同的道具可以匹配升级为新的道具。动物如果被困住,也可以合并。 羽毛: -移动(android/ios)就绪。 - 包含所有源代码。 -超过 12 座建筑/军团需要升级。 -三种特殊物品可以提供帮助。 - 三个不同的主题(场景和动物) -unity iap 支持 -Unity UI -广告位已准备好 -包含详细文档
内容概要:本文档是一份针对Java初学者的基础测试题,分为不定项选择题、简答题和编程题三大部分。选择题涵盖标识符、数组初始化、面向对象概念、运算符优先级、循环结构、对象行为、变量命名规则、基本
内容概要:本文详细介绍了如何利用MATLAB进行机器人运动学、动力学以及轨迹规划的建模与仿真。首先,通过具体的代码实例展示了正运动学和逆运动学的实现方法,包括使用DH参数建立机械臂模型、计算末端位姿以及求解关节角度。接着,讨论了雅克比矩阵的应用及其在速度控制中的重要性,并解释了如何检测和处理奇异位形。然后,深入探讨了动力学建模的方法,如使用拉格朗日方程和符号工具箱自动生成动力学方程。此外,还介绍了多种轨迹规划技术,包括抛物线插值和五次多项式插值,确保路径平滑性和可控性。最后,提供了常见仿真问题的解决方案,强调了在实际工程项目中需要注意的关键点。 适合人群:对机器人控制感兴趣的初学者、希望深入了解机器人运动学和动力学的学生及研究人员、从事机器人开发的技术人员。 使用场景及目标:① 学习如何使用MATLAB进行机器人运动学、动力学建模;② 掌握不同类型的轨迹规划方法及其应用场景;③ 解决仿真过程中遇到的各种问题,提高仿真的稳定性和准确性。 其他说明:文中提供的代码片段可以直接用于实验和教学,帮助读者更好地理解和掌握相关概念和技术。同时,针对实际应用中的挑战提出了实用的建议,有助于提升项目的成功率。
包括:源程序工程文件、Proteus仿真工程文件、配套技术手册等 1、采用51/52单片机作为主控芯片; 2、发送机:18B20测温、开关模拟灯光,发送数据; 3、接收机:接受数据、12864液晶显示;
内容概要:本文探讨了在微电网优化中如何处理风光能源的不确定性,特别是通过引入机会约束和概率序列的方法。首先介绍了风光能源的随机性和波动性带来的挑战,然后详细解释了机会约束的概念,即在一定概率水平下放松约束条件,从而提高模型灵活性。接着讨论了概率序列的应用,它通过对历史数据分析生成多个可能的风光发电场景及其概率,以此为基础构建优化模型的目标函数和约束条件。文中提供了具体的Matlab代码示例,演示了如何利用CPLEX求解器解决此类优化问题,并强调了参数选择、模型构建、约束添加以及求解过程中应注意的技术细节。此外,还提到了一些实用技巧,如通过调整MIP gap提升求解效率,使用K-means聚类减少场景数量以降低计算复杂度等。 适合人群:从事电力系统研究、微电网设计与运营的专业人士,尤其是那些对风光不确定性建模感兴趣的研究者和技术人员。 使用场景及目标:适用于需要评估和优化含有大量间歇性可再生能源接入的微电网系统,旨在提高系统的经济性和稳定性,确保在面对风光出力波动时仍能维持正常运作。 其他说明:文中提到的方法不仅有助于学术研究,也可应用于实际工程项目中,帮助工程师们制定更为稳健的微电网调度计划。同时,文中提供的代码片段可供读者参考并应用于类似的问题情境中。
linux之用户管理教程.md
内容概要:本文详细介绍了如何利用组态王和西门子S7-200 PLC构建六层或八层电梯控制系统。首先进行合理的IO地址分配,明确输入输出信号的功能及其对应的物理地址。接着深入解析了PLC源代码的关键部分,涵盖初始化、呼叫处理、电梯运行逻辑和平层处理等方面。此外,提供了组态王源代码用于实现动画仿真,展示了电梯轿厢的画面创建及动画连接方法。最后附上了详细的电气原理图和布局图,帮助理解和实施整个系统架构。 适合人群:从事工业自动化控制领域的工程师和技术人员,尤其是对PLC编程和人机界面开发感兴趣的从业者。 使用场景及目标:适用于教学培训、工程项目实践以及研究开发等场合。旨在为相关人员提供一个完整的电梯控制系统设计方案,便于他们掌握PLC编程技巧、熟悉组态软件的应用,并能够独立完成类似项目的开发。 其他说明:文中不仅包含了理论知识讲解,还分享了许多实际操作经验,如解决编码器丢脉冲的问题、优化平层停车精度的方法等。同时强调了安全性和可靠性方面的考虑,例如设置了多重保护机制以确保系统稳定运行。
在工业生产和设备运行过程中,滚动轴承故障、变压器油气故障等领域的数据分类与故障诊断至关重要。准确的数据分类与故障诊断能够及时发现设备潜在问题,避免故障恶化导致的生产事故与经济损失。LSTM能够捕获时序信息,马尔可夫场(MTF)能够一维信号转换为二维特征图,并结合CNN学习空间特征,MTF-1D-2D-CNN-LSTM-Attention模型通过将一维时序信号和二维图像融合,融合不同模态优势,并引入多头自注意力机制提高泛化能力,为数据分类与故障诊断提供了新的思路。实验结果表明,该模型在分类准确率、鲁棒性和泛化能力方面具有显著优势。多模态融合算法凭借其创新点和实验验证的有效性,在滚动轴承故障、变压器油气故障等领域展现出广阔的应用前景,有望推动相关领域故障诊断技术的进一步发展。 关键词:多模态融合;故障诊断;马尔可夫场;卷积神经网络;长短期记忆神经网络 适用平台:Matlab2023版本及以上。实验硬件设备配置如下:选用高性能计算机,搭载i7处理器,以确保数据处理和模型训练的高效性;配备16GB的内存,满足大规模数据加载和模型运算过程中的内存需求;使用高性能显卡,提供强大的并行计算能力,加速深度学习模型的训练过程。实验参数的选择依据多方面因素确定。
内容概要:本文档提供了一个面试模拟的指导框架,旨在为用户提供一个真实的面试体验。文档中的面试官名为Elian,被设定为性格温和冷静且思路清晰的形象,其主要职责是根据用户提供的简历信息和应聘岗位要求,进行一对一的模拟面试。面试官将逐一提出问题,确保每次只提一个问题,并等待候选人的回答结束后再继续下一个问题。面试官需要深入了解应聘岗位的具体要求,包括但不限于业务理解、行业知识、具体技能、专业背景以及项目经历等方面,从而全面评估候选人是否符合岗位需求。此外,文档强调了面试官应在用户主动发起提问后才开始回答,若用户未提供简历,面试官应首先邀请用户提供简历或描述应聘岗位; 适用人群:即将参加面试的求职者,特别是希望提前熟悉面试流程、提升面试技巧的人士; 使用场景及目标:①帮助求职者熟悉面试流程,提高应对实际面试的信心;②通过模拟面试,让求职者能够更好地展示自己的优势,发现自身不足之处并加以改进; 其他说明:此文档为文本格式,用户可以根据文档内容与面试官Elian进行互动,以达到最佳的模拟效果。在整个模拟过程中,用户应尽量真实地回答每一个问题,以便获得最贴近实际情况的反馈。
招聘技巧HR必看如何进行网络招聘和电话邀约.ppt
内容概要:本文详细介绍了利用三菱PLC(特别是FX系列)和组态王软件构建3x3书架式堆垛式立体库的方法。首先阐述了IO分配的原则,明确了输入输出信号的功能,如仓位检测、堆垛机运动控制等。接着深入解析了梯形图编程的具体实现,包括基本的左右移动控制、复杂的自动寻址逻辑,以及确保安全性的限位保护措施。还展示了接线图和原理图的作用,强调了正确的电气连接方式。最后讲解了组态王的画面设计技巧,通过图形化界面实现对立体库的操作和监控。 适用人群:从事自动化仓储系统设计、安装、调试的技术人员,尤其是熟悉三菱PLC和组态王的工程师。 使用场景及目标:适用于需要提高仓库空间利用率的小型仓储环境,旨在帮助技术人员掌握从硬件选型、电路设计到软件编程的全流程技能,最终实现高效稳定的自动化仓储管理。 其他说明:文中提供了多个实用的编程技巧和注意事项,如避免常见错误、优化性能参数等,有助于减少实际应用中的故障率并提升系统的可靠性。
内容概要:本文详细探讨了利用COMSOL进行电弧放电现象的模拟,重点在于采用磁流体方程(MHD)来耦合电磁、热流体和电路等多个物理场。文中介绍了关键的数学模型如磁流体动力学方程、热传导方程以及电路方程,并讨论了求解过程中遇到的技术难题,包括参数敏感性、求解器选择、网格划分等问题。此外,作者分享了许多实践经验,比如如何处理不同物理场之间的相互作用,怎样避免数值不稳定性和提高计算效率。 适用人群:适用于从事电弧放电研究的专业人士,尤其是那些希望通过数值模拟深入了解电弧行为并应用于实际工程项目的人群。 使用场景及目标:①帮助研究人员更好地理解和预测电弧放电过程中的各种物理现象;②为工程师提供优化电气设备设计的方法论支持;③指导使用者正确配置COMSOL软件的相关参数以确保高效稳定的仿真结果。 其他说明:尽管存在较高的计算复杂度和技术挑战,成功的电弧放电仿真能够显著提升对这一重要物理过程的认识水平,并促进相关领域的技术创新和发展。
内容概要:本文详细介绍了如何利用粒子群优化算法(PSO)改进极限学习机(KELM),以提升其在多维输入单维输出数据处理任务中的性能。首先简述了KELM的工作原理及其快速训练的特点,接着深入探讨了PSO算法的机制,包括粒子的速度和位置更新规则。然后展示了如何将PSO应用于优化KELM的关键参数,如输入权值和隐含层偏置,并提供了具体的Python代码实现。通过对模拟数据和实际数据集的实验对比,证明了PSO优化后的KELM在预测精度上有显著提升,尤其是在处理复杂数据时表现出色。 适合人群:对机器学习尤其是深度学习有一定了解的研究人员和技术爱好者,以及从事数据分析工作的专业人士。 使用场景及目标:适用于需要高效处理多维输入单维输出数据的任务,如时间序列预测、回归分析等。主要目标是通过优化模型参数,提高预测准确性并减少人工调参的时间成本。 其他说明:文中不仅给出了详细的理论解释,还附上了完整的代码示例,便于读者理解和实践。此外,还讨论了一些实用技巧,如参数选择、数据预处理等,有助于解决实际应用中的常见问题。
内容概要:本文介绍了利用粒子群算法(PSO)解决微网优化调度问题的方法。主要内容涵盖微网系统的组成(风力、光伏、储能、燃气轮机、柴油机)、需求响应机制、储能SOC约束处理及粒子群算法的具体实现。文中详细描述了目标函数的设计,包括发电成本、启停成本、需求响应惩罚项和SOC连续性惩罚项的计算方法。同时,阐述了粒子群算法的核心迭代逻辑及其参数调整策略,如惯性权重的线性递减策略。此外,还讨论了代码调试过程中遇到的问题及解决方案,并展示了仿真结果,证明了模型的有效性和优越性。 适合人群:从事电力系统优化、智能算法应用的研究人员和技术人员,特别是对微网调度感兴趣的读者。 使用场景及目标:适用于研究和开发微网优化调度系统,旨在提高供电稳定性的同时降低成本。具体应用场景包括但不限于分布式能源管理、工业园区能源调度等。目标是通过合理的调度策略,使微网系统在满足需求响应的前提下,实现经济效益最大化。 其他说明:本文提供的Matlab程序具有良好的模块化设计,便于扩展和维护。建议读者在理解和掌握基本原理的基础上,结合实际情况进行改进和创新。
KUKA机器人相关资料
基于多智能体的高层建筑分阶段火灾疏散仿 真及策略研究.pdf
Iterative Time Series Imputation by Maintaining Dependency Consistency (ACM TKDD 2024)
内容概要:本文详细探讨了带同步整流桥的交错PFC(功率因数校正)电路的设计与仿真实现。交错PFC通过多路PFC电路交错工作,降低了输入电流纹波,提高了功率密度。同步整流桥采用MOSFET代替传统二极管,减少了整流损耗,提升了效率。文中提供了关键代码片段,包括PWM控制、同步整流桥控制逻辑、电流环控制等,并介绍了如何在MATLAB/Simulink中搭建仿真模型,验证设计方案的有效性。此外,还讨论了仿真过程中遇到的问题及其解决方案,如死区时间处理、电流采样精度、负载突变应对等。 适合人群:从事电力电子设计的研究人员和技术工程师,尤其是对PFC技术和同步整流感兴趣的从业者。 使用场景及目标:适用于研究和开发高效的电源管理系统,旨在提高电能利用率,减少谐波污染,优化电源性能。目标是通过仿真实验验证设计方案的可行性,最终应用于实际硬件开发。 其他说明:文章强调了仿真与实际调试的区别,提醒读者在实际应用中需要注意的细节,如电流采样精度、死区时间和负载突变等问题。同时,提供了具体的代码实现和仿真技巧,帮助读者更好地理解和掌握这一复杂的技术。
内容概要:本文详细探讨了MATLAB环境下冷热电气多能互补微能源网的鲁棒优化调度模型。首先介绍了多能耦合元件(如风电、光伏、P2G、燃气轮机等)的运行特性模型,展示了如何通过MATLAB代码模拟这些元件的实际运行情况。接着阐述了电、热、冷、气四者的稳态能流模型及其相互关系,特别是热电联产过程中能流的转换和流动。然后重点讨论了考虑经济成本和碳排放最优的优化调度模型,利用MATLAB优化工具箱求解多目标优化问题,确保各能源设备在合理范围内运行并保持能流平衡。最后分享了一些实际应用中的经验和技巧,如处理风光出力预测误差、非线性约束、多能流耦合等。 适合人群:从事能源系统研究、优化调度、MATLAB编程的专业人士和技术爱好者。 使用场景及目标:适用于希望深入了解综合能源系统优化调度的研究人员和工程师。目标是掌握如何在MATLAB中构建和求解复杂的多能互补优化调度模型,提高能源利用效率,降低碳排放。 其他说明:文中提供了大量MATLAB代码片段,帮助读者更好地理解和实践所介绍的内容。此外,还提及了一些有趣的发现和挑战,如多能流耦合的复杂性、鲁棒优化的应用等。
内容概要:本文详细介绍了如何在Simulink中构建永磁同步电机(PMSM)无位置传感器的磁场定向控制(FOC)系统。主要内容涵盖双闭环PI调节器的设计、SVPWM调制方法、坐标变换、滑模观测器用于无位置估算以及各环节常见问题及其解决方案。文中提供了具体的MATLAB代码示例,如Clarke变换、SVPWM扇区判断、PI调节器抗饱和处理等,并分享了许多实用的调试技巧,如电流环积分限幅、SVPWM扇区判断优化、滑模观测器增益选择等。 适合人群:具有一定电机控制基础的研究人员和技术工程师,尤其是从事电力电子、自动化控制领域的专业人士。 使用场景及目标:适用于希望深入理解并掌握PMSM无位置传感器FOC控制系统的开发者。主要目标是在Simulink环境中搭建完整的FOC控制系统,解决实际应用中的各种技术难点,提高系统的稳定性和精度。 其他说明:文章强调了仿真与实际硬件之间的差异,指出了一些常见的陷阱和应对措施。同时,作者分享了很多个人实践经验,使得复杂的技术概念更加通俗易懂。