静态导入(static import)
静态导入的形式:import static 包路径.类;
与普通的导入方式相比仅多了个static
,还有就是ClassName(类名)后面多了个 .星号
,意思是导入这个类里的所有静态方法。当然也可以只导入某个静态方法,只要把.星号
换成静态方法名就行了。
静态导入之后在类中就可以直接用方法名调用静态方法,而不必用 类名.方法名
的方式调用,同样静态的变量也可以直接使用。
注意:这样的写法虽然可以简化代码的书写,但是代码的可读性下降。
自动装箱与拆箱(Autoboxing and unboxing)
- JAVA 包装类型 及 易错陷阱
- 《编写高质量代码-改善Java程序的151个建议》
- 建议26:堤防包装类型的null值
- 建议27:谨防包装类型的大小比较
For-Each循环
For-Each语句语法:for ( variable : collection ) statement
这是一种循环结构,可以用来依次处理数组中的每个元素而不必为指定下标值而分心。
我们需要定义一个变量(variable)用于暂存集合或数组中的每个元素,并执行相应的语句(statement),collection这个集合表达式必须是一个数组/集合或者是一个实现了Iterable
接口的类对象。
- 优点:与传统的循环相比语句更加简洁、更不易出错(不必为下标的起始位置和终止值而操心)。
- 缺点:for-each结构会遍历集合中的所有元素,如果希望遍历部分元素或者在循环内部需要使用下标值,就力不从心了,传统的循环就显示出优势了。
// 遍历数组
int arr[] = { 2, 5, 3 };
for (int x : arr) {
System.out.print(x + " ");
}
// 遍历二维数组
int arr[][] = { { 1, 2 }, { 3, 4 } };
for (int[] x : arr) {
for (int e : x) {
System.out.print(e + " ");
}
System.out.println();// 换行
}
// 遍历集合
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("5");
for (String x : list) {
System.out.println(x + " ");
}
// 集合转换为集合并遍历
Object[] obj = list.toArray();
for (Object x : obj) {
System.out.print(x.toString() + " ");
}
枚举(enum)
枚举类型的定义:enum <枚举类型名> {<枚举表>};
可变长度参数(Varargs)
在编写功能相同,但是参数数量不同的函数的时候,作为一名程序员,我是有想偷懒的想法,可变长度参数便是为此提供的一种简化的方式。
我们只需要编写内部的方法即可,函数可以接收数量未知的参数。
可变长度参数定义格式:Object...
此处的Object
可以是任何类型只不过在后面添加三个点就可以了,表明可以接收任意数量的该对象,上面的Object...
参数类型与Object[]
完全一样。
//计算若干个数值的最大值
public static double max(double... values){
double largest = Double.MIN_VALUE;
for(double v : values){
if (v>largest) {
largest=v;
}
}
return largest;
}
注意:当多个参数时,可变参数必须是参数列表的最后一个参数。
避免模糊重载
// Varargs, overloading, and ambiguity.
//
// This program contains an error and will
// not compile!
class VarArgs4 {
static void vaTest(int ... v) {
System.out.print("vaTest(Integer ...): " +
"Number of args: " + v.length +
" Contents: ");
for(int x : v)
System.out.print(x + " ");
System.out.println();
}
static void vaTest(boolean ... v) {
System.out.print("vaTest(boolean ...) " +
"Number of args: " + v.length +
" Contents: ");
for(boolean x : v)
System.out.print(x + " ");
System.out.println();
}
public static void main(String args[]){
vaTest(1, 2, 3); // OK
vaTest(true, false, false); // OK
vaTest(); // Error: Ambiguous!
}
}
上面代码的出错原因是在参数为空的情况下,两个重载的vaTes
方法都可以匹配。
另一种类似的模糊情况是:
static void vaTest(int... v);
static void vaTest(int n,int... v)
// 调用
vaTest(1);
内省(Introspector)
内省是Java对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name
,那我们可以通过getName
,setName
来得到其值或者设置新的值。通过getName/setName
来访问name
属性,这就是默认的规则。
Java中提供了一套API用来访问某个属性的getter /setter
方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans
中。
一般的做法是通过类Introspector
来获取某个对象的BeanInfo
信息,然后通过BeanInfo
来获取属性的描述器 (PropertyDescriptor
),通过这个属性描述器就可以获取某个属性对应的getter/setter
方法,然后我们就可以通过反射机制来调用这些方法。
内省与反射的区别:
- 反射可以操作各种类的属性,而内省只是通过反射来操作JavaBean的属性
- 内省设置属性值肯定会调用setter方法,反射可以不用
// JavaBean类
public class User {
private String name;
private int age;
private Date birthday;
// 省略getName/setName方法
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", birthday=" + birthday + "]";
}
}
通过反射机制操作name
属性:
User user = new User();
Field f = user.getClass().getDeclaredField("name");
f.setAccessible(true);
f.set(user, "mld");// 设置属性值
String name = (String) f.get(user);// 获取属性值
通过内省操作name
属性:
User user = new User();
// 操作单个属性
PropertyDescriptor pd = new PropertyDescriptor("name", User.class);
Method w = pd.getWriteMethod();// 获取属性的setter方法
w.invoke(user, "winclpt");
Method r = pd.getReadMethod();// 获取属性的getter方法
r.invoke(user, null);
// 操作所有属性
BeanInfo bi = Introspector.getBeanInfo(User.class);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor p : pds) {
}
其它示例:
/**
* @Description: JavaBean属性操作工具类
*/
public class MyBeanUtils {
/**
* @Description: 根据指定的属性名称获取属性值
* @Parameters: @param propertyName 属性名称
* @Parameters: @param bean bean实例对象
* @Return: Object 返回getter方法的返回值,即属性值
*/
public static Object getPropertyValue(String propertyName, Object bean) {
Object propertyValue = null;
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass());
Method method = pd.getReadMethod();
propertyValue = method.invoke(bean);
} catch (Exception e) {
e.printStackTrace();
}
return propertyValue;
}
/**
* @Description: 设置/修改属性的内容
* @Parameters: @param bean Bean实例对象
* @Parameters: @param name 属性名
* @Parameters: @param value 修改内容
*/
public static void setProperty(Object bean, String name, Object value) {
try {
PropertyDescriptor pd = new PropertyDescriptor(name, bean.getClass());
Method method = pd.getWriteMethod();
method.invoke(bean, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description:
* 将Map中的内容封装到 JavaBean 说明:
* Map中的key必须与JavaBean中的属性名称相同
* Map中的value传递给JavaBean对应的属性
* @Parameters: @param bean
* @Parameters: @param map
*/
public static Object populate(Object bean, Map<String, Object> map) {
if (map != null && map.size() > 0) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String propertyName = entry.getKey();
Object propertyValue = entry.getValue();
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName, bean.getClass());
Method method = pd.getWriteMethod();
method.invoke(bean, propertyValue);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return bean;
}
}
/**
* @Description: JavaBean类定义
*/
public class User {
String name;
int age;
boolean gender;
Date birthday;
String address;
// 省略getName/setName方法
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ",gender=" + gender + ", birthday=" + birthday + ",address=" + address + "]";
}
}
/**
* @Description: JavaBean-API:内省机制测试类
*/
public class IntrospectorTest {
private User user;
@Before
public void init() {
user = new User();
user.setName("张三");
user.setAge(21);
user.setGender(true);
user.setBirthday(new Date());
user.setAddress("北京丰台");
}
/**
* @Description: 获取User-Bean的所有属性信息
* @Parameters: @throws Exception
*/
@Test
public void getBeanPropertyInfo() throws Exception {
// 获取User-BeanInfo对象:beanInfo是对一个Bean的描述,可以通过它取得Bean内部的信息
/**
* 获取User-BeanInfo对象
* 1、Introspector类是一个工具类,提供了一系列取得BeanInfo的方法;
* 2、BeanInfo接口对一个JavaBean的描述,可以通过它取得Bean内部的信息;
* 3、PropertyDescriptor属性描述器类对一个Bean属性的描述,它提供了一系列对Bean属性进行操作的方法
*/
BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class);
PropertyDescriptor[] pds = userBeanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
Method method = pd.getReadMethod();
String methodName = method.getName();
Object result = method.invoke(user);
System.out.println(methodName + " -> " + result);
}
}
/**
* @Description: 获取指定属性名称的属性描述器,并对属性进行操作
*/
@Test
public void getBeanPropertyByName() throws Exception {
// 获取name属性的属性描述器
PropertyDescriptor pd = new PropertyDescriptor("name", user.getClass());
// 得到name属性的getter方法
Method readMethod = pd.getReadMethod();
// 执行getter方法,获取返回值,即name属性的值
String result = (String) readMethod.invoke(user);
System.out.println("user.name" + " -> " + result);
// 得到name属性的setter方法
Method writeMethod = pd.getWriteMethod();
// 执行setter方法,修改name属性的值
writeMethod.invoke(user, "李四");
System.out.println("user.name" + " -> " + user.getName());
}
}
/**
* @Description: JavaBean属性操作工具测试类
*/
public class MyBeanUtilsTest {
private User user;
@Before
public void init() {
user = new User();
user.setName("张三");
user.setAge(21);
user.setGender(true);
user.setBirthday(new Date());
user.setAddress("北京丰台");
}
@Test
public void testSetPropertyValue() {
// 设置String类型数据
MyBeanUtils.setProperty(user, "name", "李思思");
// 设置int类型数据
MyBeanUtils.setProperty(user, "age", 23);
// 设置boolean类型数据
MyBeanUtils.setProperty(user, "gender", false);
// 设置Date类型数据
MyBeanUtils.setProperty(user, "birthday", new Date(13213412412L));
System.out.println(user);
}
@Test
public void testGetPropertyValue() {
// 获取String类型属性
String name = (String) MyBeanUtils.getPropertyValue("name", user);
// 获取int类型属性
int age = (Integer) MyBeanUtils.getPropertyValue("age", user);
// 获取boolean类型属性
boolean gender = (Boolean) MyBeanUtils.getPropertyValue("gender", user);
// 获取Date类型属性
Date birthday = (Date) MyBeanUtils.getPropertyValue("birthday", user);
System.out.println(name + "、" + age + "、" + gender + "、" + birthday + "。");
}
@Test
public void testPopulate() {
// 向Map集合中封装数据,适用于request.getParameterMap() ;
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "王五");
map.put("age", 21);
map.put("gender", true);
map.put("birthday", new Date(32131412L));
map.put("address", "上海");
// 将Map集合中的数据封装到UserBean
User u = (User) MyBeanUtils.populate(new User(), map);
System.out.println(u);
}
}
相对而言内省操作比较繁琐,而Apache Commons
为我们提供了一套简单、易用的API来操作Bean的属性的工具包BeanUtils。
更多关于BeanUtils的使用可查看:Commons BeanUtils 用户指南
Java 注解(Annotation)
Java Web Start
格式化 I/O
对于了解 C 语言的人来说,printf 这个输出并不陌生,Java也沿用了这种方法。
public class ceshi{
public static void main(String[] arg){
display("fxb1",32);
display("fxb2",33);
display("fxb3",34);
}
public static void display(String name,int age){
System.out.printf("你好,%s.下一年你就%d\n",name,age);
System.out.println("------------------------------");
}
}/*输出:
你好,fxb1.下一年你就32
------------------------------
你好,fxb2.下一年你就33
------------------------------
你好,fxb3.下一年你就34
------------------------------
*/
每一个以%字符开始的格式说明符都用相应的参数替换,格式说明符尾部的转换符将指示被格式化的数值类型。
用于printf的转换符:
- d:十进制整数
- x:十六进制整数
- o:八进制整数
- f:定点浮点数
- e:指数浮点数
- g:通用浮点数
- a:十六进制浮点数
- s:字符串
- c:字符
- b:布尔值
- h:散列码
- tx:日期时间(以t开头后面的值有很多)-可参考《Java核心技术 卷一》的58页
- %:百分号
- n:与平台有关的行分隔符
System.out.printf("%+,.2f %+,.2f", -10000.0 / 3.0, 10000.0 / 3.0);
/*输出结果:
* -3,333.33 +3,333.33
*/
用于printf的标志:
- + :打印整数和负数的符号
- 空格 :在整数之前添加空格
- 0 :数字前面补0
- - :左对齐
- ( :添加分组分隔符
- #(对于f格式) :包含小数点
- #(对于x或0格式):添加前缀0x或0
- d,%1$x将以十进制和十六进制格式打印第一个参数
- <:格式化前面说明的数值。例如,%d%
泛型(Generic)
- 详细可参考:Java 泛型详解
参考资料: