所谓新特性是指从JDK1.5之后开始的
可变参数
如果说现在有这样一个要求,要求实现整数的加法操作,并且方法可以接受任意多个整形数据一起实现加法操作,如果按照传统思路,现在实现方式可以采用数组作为参数的类型。
package cn.mldn.demo;
public class TextDemo {
public static void main(String args[]){
System.out.println(add(new int[] {1,2,3}));
System.out.println(add(new int[] {1,2,3,4}));
}
public static int add (int data[]){
int sum = 0;
for(int x=0;x<data.length;x++){
sum = sum + data[x];
}
return sum;
}
}
此处使用数组完全属于无奈,虽然实现了操作,但题目本身要求,可以接收任意多个整形数据,每一个数据之间应该使用“,”分隔才最合适,所以为解决此问题,自JDK1.5之后增加了可变参数方法的定义
[ public | protected | private ] [ Final ] [ static ] 返回值类型 方法名称(参数类型 ... 参数名称)[ throws 异常,异常 ...] { [ return [ 返回值 ] ; ] } |
虽然可变数组语法形式上有些别扭,但是可以把它当作数组来理解
范例:使用可变参数
package cn.mldn.demo;
public class TextDemo {
public static void main(String args[]){
System.out.println(add(new int[] {1,2,3}));
System.out.println(add(new int[] {1,2,3,4}));
System.out.println(add(1,2,3,4));
}
public static int add (int ... data){
int sum = 0;
for(int x=0;x<data.length;x++){
sum = sum + data[x];
}
return sum;
}
}
可变参数的最大好处在于方法调用时,可以随意编写参数进行数据的传递,但是此类操作只会在系统类库的学习中遇见
foreach输出
如果说现在有一个数组要实现数据的输出,一定使用for循环完成
例如:使用for循环输出一个数组
package cn.mldn.demo;
public class TextDemo {
public static void main(String args[]){
int data[] = new int [] {1,2,3,4};
for (int x = 0;x< data.length ; x++){
System.out.println(data[x]);
}
}
}
而从JDK1.5之后增加了一类的输出结构
for (数据类型 变量 :数组|集合){ //操作代码; } |
本程序的意思是根据数组的长度进行循环操作,并且每次循环的时候取出数组之中的每一个元素,将其赋值给生命的变量
范例:使用foreach输出
package cn.mldn.demo;
public class TextDemo {
public static void main(String args[]){
int data[] = new int [] {1,2,3,4};
for (int x :data){
System.out.println(x);
}
}
}
静态导入
首先我们来观察一个全部是static方法的类
package cn.mldn.util;
public class MyMath {
public static int add (int x, int y){
return x+y;
}
public static int sub (int x,int y){
return x-y;
}
}
如果在传统情况下,要使用以上类之中所定义的方法,应该先导入包.类,而后通过类名称调用
package cn.mldn.demo;
import cn.mldn .util.MyMath;
public class TextDemo {
public static void main(String args[]){
System.out.println(MyMath.add(10,20));
System.out.println(MyMath.sub(50,20));
}
}
在之前学习过,如果在主类中定义的方法,并且由主方法直接调用的话,则方法上必须使用static,那么能不能把以上导入包中的static方法不使用使用类名称世界调用呢(相当于将方法直接定义在主类之中)
范例:静态导入
package cn.mldn.demo;
import static cn.mldn .util.MyMath.* ;
public class TextDemo {
public static void main(String[] args){
System.out.println(add(10,20));
System.out.println(sub(50,20));
}
}
此时调用方法不再加入类名称,就好比这些方法定义在主方法中一样
泛型的引出
现在要求用户设计一个表示坐标轴的类,但是次坐标保存数据有可能有如下几种情况
- 整型数据:x = 10, y = 30
- 小数数据:x =10.4,y = 20.9
- 字符串数据:x = 东经100度,y = 北纬20度
那么现在很明显,Point类表示坐标,那么针对于坐标点有三类数据,而在Point类里面应该有两个属性 x、y ,所以现在首先要解决的问题就是确定出x、y属性的数据类型
既然现在要保存有int、double、String或者以后有可能的其他类型,则自然想到使用Object,因为存在如下转换关系
- 保存整型:int→ 自动装箱为 Integer→ 向上转型为Object;
- 保存小数:double→ 自动装箱为Double→向上转型为Object;
- 保存字符串:String→向上转型为Object。
范例,实现代码
package cn.mldn.demo;
class Point{
private Object x;
private Object y;
public void setX(Object x ){
this.x = x;
}
public void setY(Object y){
this.y = y;
}
public Object getX(){
return x;
}
public Object getY(){
return y;
}
}
public class TextDemo {
public static void main(String[] args){
//第一层次:设置坐标数据
Point point = new Point() ;
point.setX(10) ; //向上转型为Object
point.setY(30) ; //向上转型为Object
//第二层次:取得做坐标数据
int x = (Integer) point.getX() ; //向下转型
int y = (Integer) point.getY() ; //向下转型
System.out.println("x =" + x + ", y =" + y);
}
}
此时代码已经完成要求,但是本程序依靠的是Object可以接收所有数据类型这一特征展开的,但Object范围太大,那么在程序运行过程中就可能出错。
范例:观察问题
public class TextDemo {
public static void main(String[] args){
//第一层次:设置坐标数据
Point point = new Point() ;
point.setX(东经100度) ; //向上转型为Object
point.setY(30) ; //向上转型为Object
//第二层次:取得做坐标数据
int x = (Integer) point.getX() ; //向下转型
int y = (Integer) point.getY() ; //向下转型
System.out.println("x =" + x + ", y =" + y);
}
此时程序编译的时候没有出现任何错误,但是执行的时候就会出现“ClassCastException”,所以来讲此时程序就存在有安全隐患,事实上,所有的向下转型都可能存在这种安全隐患,最好的做法就是不转型。
而在JDK1.5之后由于引进了泛型的处理机制,所以此类问题很好解决了,所谓泛型指的就是类之中定义的属性,在程序编译时不会给出具体的类型,只给处一个类型的占位标记,而后在使用此类产生对象时,在设置具体类型
范例:利用泛型修改
package cn.mldn.demo;
class Point<T>{ //T只是一个标记,并不是类型
private T x;
private T y;
public void setX(T x ){
this.x = x;
}
public void setY(T y){
this.y = y;
}
public T getX(){
return x;
}
public T getY(){
return y;
}
}
public class TextDemo {
public static void main(String[] args){
//第一层次:设置坐标数据
Point<String> point = new Point<String>() ;
point.setX("东经100度") ;
point.setY("北纬20度") ;
//第二层次:取得做坐标数据
String x = point.getX() ; //向下转型
String y = point.getY() ; //向下转型
System.out.println("x = " + x + ", y = " + y);
}
}
那么此时由于泛型技术的出现,取消了向下转型,而向下转型的取消就相当与消除了所有的“ClassCastException”这种安全隐患,但需要注意的是,在设置泛型类型的时候只能够使用引用数据类型,即:如果要保存int或者double,用包装类操作
范例:保存int
public class TextDemo {
public static void main(String[] args){
//第一层次:设置坐标数据
Point<Integer> point = new Point<Integer>() ;
point.setX(10) ;
point.setY(20) ;
//第二层次:取得做坐标数据
int x = point.getX() ;
int y = point.getY() ;
System.out.println("x = " + x + ", y = " + y);
}
}
但是为了保证以前的代码没有任何错误,所以如果在使用泛型标记类的时候没有设置泛型类型,那么会按照Object做默认处理
public class TextDemo {
public static void main(String[] args){
//第一层次:设置坐标数据
Point point = new Point() ;
point.setX(10) ;
point.setY(20) ;
//第二层次:取得做坐标数据
int x = (Integer)point.getX() ;
int y = (Integer)point.getY() ;
System.out.println("x = " + x + ", y = " + y);
}
}
所以不设置泛型请一定要记住,类型就是Object
通配符
为了方便,重新定义一个泛型的类并且针对于Info类实现一次引用传递
package cn.mldn.demo;
class Info<T>{
private T msg;
public void setMsg(T msg){
this.msg = msg;
}
public T getMsg(){
return msg;
}
}
public class TextDemo {
public static void main(String[] args){
Info<String> info = new Info<String>() ;
info.setMsg("Hello world!") ;
fun(info);
}
public static void fun(Info<String> temp){
System.out.println(temp.getMsg());
}
}
既然是设置泛型,肯定不可能只是String一种,有可能设置其他类型,此时如果传递的是一个“Info<Integer>”那么fun()方法是不可能使用的。用重载也不可能,因为重载看重的是数据类型,于是发现,一旦使用泛型,在之前好不容易解决的参数统一问题又出来了,但是如果参数上不设置泛型,那么就表示的类型就是Object
范例:参数上不使用泛型
public class TextDemo {
public static void main(String[] args){
Info<String> info = new Info<String>() ;
info.setMsg("Hello world!") ;
fun(info);
}
public static void fun(Info temp){
temp.setMsg(100); //现在修改为Integer
System.out.println(temp.getMsg());
}
}
所以这个时候发现,不设置泛型,操作的数据形式就可能出现混乱,于是现在就可以总结出来,我们需要一个可以接收任意任意的泛型类型,但是又不能修改里面的数据,并且可以取得里面数据的操作。那么现在就只能够利用通配符“?”来完成此功能。
范例:使用“?”解决问题
public class TextDemo {
public static void main(String[] args){
Info<String> info = new Info<String>() ;
info.setMsg("Hello world!") ;
fun(info);
}
public static void fun(Info<?> temp){
System.out.println(temp.getMsg());
}
}
但是在“?”的基础上又扩充了两个子的通配符:
- 设置泛型上线:?extends 类(Textends 类);例如:“T extends Number”,表示此处只能够设置Number和Number的子类(Integer);
- 设置泛型下线:?super String,表示只能够设置String或者String的父类Object。
范例:设置泛型上线
package cn.mldn.demo;
class Info<T extends Number>{
private T msg;
public void setMsg(T msg){
this.msg = msg;
}
public T getMsg(){
return msg;
}
}
public class TextDemo {
public static void main(String[] args){
Info<Integer> info = new Info<Integer>() ;
info.setMsg(100) ;
fun(info);
}
public static void fun(Info<? extends Number> temp){
System.out.println(temp.getMsg());
}
}
范例:设置泛型下线
package cn.mldn.demo;
class Info<T>{
private T msg;
public void setMsg(T msg){
this.msg = msg;
}
public T getMsg(){
return msg;
}
}
public class TextDemo {
public static void main(String[] args){
Info<String> info = new Info<String>() ;
info.setMsg("Hello world!") ;
fun(info);
}
public static void fun(Info<? super String> temp){
System.out.println(temp.getMsg());
}
}
泛型接口
在之前所创建的泛型都是在类的定义上使用的,那么也可以在接口上使用泛型。
范例:在接口上使用泛型
interface Message<T>{
public void print(T msg);
}
那么此时对于这样的接口有两种实现方式:
方式一:在子类实现接口后继续使用泛型
package cn.mldn.demo;
interface Message<T>{
public void print(T msg);
}
class MessageImpl<T> implements Message<T>{
public void print(T msg){
System.out.println(msg);
}
}
public class TextDemo {
public static void main(String[] args){
Message<String> message = new MessageImpl<String>();
message.print("Hello World");
}
}
方式二:子类直接设置具体的泛型类型
package cn.mldn.demo;
interface Message<T>{
public void print(T msg);
}
class MessageImpl implements Message<String>{
public void print(String msg){
System.out.println(msg);
}
}
public class TextDemo {
public static void main(String[] args){
Message<String> message = new MessageImpl();
message.print("Hello World");
}
}
泛型方法
在之前已经接触过泛型方法,但是所有接触到的泛型方法都是声明在泛型类之中,而现在所讲泛型方法指的是没有泛型声明的类上定义的方法。
范例:定义泛型方法
public class TextDemo {
public static void main(String[] args){
Integer temp[] = fun(1,2,3);
for(Integer x : temp){
System.out.println(x);
}
}
public static <T> T[] fun(T ... args){
return args;
}
}
枚举
多例设计模式典型特点:构造方法私有化,而后在类的内部实例化好若干个对象,并且通过static方法返回,可是如果按照之前编写的多礼设计实际上是存在问题的。
范例:观察问题
package cn.mldn.demo;
class Color{
private String title;
private static final Color RED = new Color("红色");
private static final Color GREEN = new Color("绿色");
private static final Color BLUE = new Color("蓝色");
private Color(String title){
this.title = title;
}
public String toString(){
return this.title;
}
public static Color getInstance(int ch){
switch(ch){
case 0 :
return RED;
case 1 :
return GREEN;
case 2 :
return BLUE;
default :
return null;
}
}
}
public class TextDemo {
public static void main(String[] args){
Color red = Color.getInstance(5);
System.out.println(red);
}
}
在之前的代码之中,实现的多例设计模式里面存在一个矛盾,发现用户实际上并不知道类中存在有多少个可用对象,而且所有的对象必须通过一个static方法,传入一些数字来取得。那么就证明传统的多例设计并不好用。所以为了解决这样的问题,从JDK1.5开始增加了一个enum的关键字,使用它可以定义枚举。
范例:定义枚举
enum Color{
RED,GREEN,BLUE;
}
枚举之中的对象都是使用String final定义的,所以一定要用大写字母表示
范例:使用枚举
package cn.mldn.demo;
enum Color{
RED,GREEN,BLUE;
}
public class TextDemo {
public static void main(String[] args){
Color red = Color.RED;
System.out.println(red);
}
}
此时可以清楚发现,直接利用枚举对象就可以取得里面保存的对象信息,同时在枚举操作的过程里面还可以取得全部对象
范例:输出枚举全部内容
package cn.mldn.demo;
enum Color{
RED,GREEN,BLUE;
}
public class TextDemo {
public static void main(String[] args){
for(Color c : Color.values()) {
System.out.println(c);
}
}
}
通过演示应该清楚了enum关键字的作用,但是严格来讲,在Java之中,使用enum关键字定义的枚举对象就严格来讲相当于是一个使用class定义的类,而后继承了Enum类是一样的。首先Enum是一个抽象类,在Enum类之中定义了如下方法
- 构造方法:protected Enum(String name,int ordinal),构造被封装,子类可以调用;
- 取得枚举名字:public final String name();
- 取得索引号:public final int ordinal()。
范例:调用Enum类的方法
package cn.mldn.demo;
enum Color{
RED,GREEN,BLUE;
}
public class TextDemo {
public static void main(String[] args){
for(Color c : Color.values()) {
System.out.println(c.name() + "," + c.ordinal());
}
}
}
enum与Enum的区别
- enum是一个关键字,而Enum是一个抽象类
- 使用enum定义的结构就相当于一个类继承了Enum类
在枚举之中定义其他结构
虽然说枚举是单例设计模式的实现,但是通过研究多例设计模式,你可以清楚的发现,多例设计模式之中,除了定义类对象之外,实际上还可以构造方法、普通方法、属性。而枚举也可以做到这些,但是有如下要求:
- 枚举之中定义多个结构的时候,枚举的对象要写在类第一行;
- 枚举毕竟属于多例设计,构造方法绝对不能使用public表示。
范例:在枚举之中定义其他结构
package cn.mldn.demo;
enum Color{
RED("红色"),GREEN("绿色"),BLUE("蓝色"); //对象
private String title;
private Color(String title){
this.title = title;
}
public String toString(){
return this.title;
}
}
而且除了以上结构之外,枚举作为一个特殊类,也可以让其实现接口
范例:让枚举实现接口
package cn.mldn.demo;
interface Message{
public String getInfo();
}
enum Color{
RED("红色"),GREEN("绿色"),BLUE("蓝色"); //对象
private String title;
private Color(String title){
this.title = title;
}
public String toString(){
return this.title;
}
public String getInfo(){
return this.title;
}
}
public class TextDemo {
public static void main(String[] args){
for(Color c : Color.values()) {
System.out.println(c);
}
}
}
Annotation
如果要想彻底理解 Annotation 必须通过开发的结构的历史做一个回顾
- 最早的时候编写代码,会将所有与系统配置的信息都写在程序代码之中;
- 随后产生了新的设计,使用一些配置文件保存用户可修改的配置信息,通过程序读取配置数据,这种开发模式虽然适合于没有意义的用户维护,但是开发太麻烦了。
- 第三个阶段:是将配置文件信息写回到程序之中,但是必须与原始程序做一个有效的分割,于是有了注解的方式(编译时的注解、运行时的注解、类级注解)。
在Java之中已经为用户提供好了三类注解:@Override、@Deprecated、@SuppressWranings。
1、准确的覆写:@Override
覆写定义:方法名称、参数类型及个数、返回值类型全部相同,而且被覆写的方法不能拥有比父类更为严格的访问控制权限。但是在实际的开发之中,有可能因为用户的手误,会写出这样的代码。
package cn.mldn.demo;
class Message{
@Override
public String toString(){ //这不是覆写
return "这是一个消息";
}
}
public class TextDemo {
public static void main(String[] args){
System.out.println(new Message());
}
}
可以在一个方法覆写时加上一个@Override,表示这个方法是覆写父类的方法
2、声明过期操作:@Deprecated
在一个系统的开发之中,一定都是不断进行完善的,那么就可能出现这样情况,例如:用户在一个系统的1.0版本提供了一个Message的类,但是这个类之中最早有了一个fun()方法,后来到了系统的3.0时代,发现Mesfun类最早设计的fun()方法有可能会存在安全隐患,于是提供了一个新的funNew()方法。但是这个时候就面临一个问题:部分的开发者已经利用了Message类编写了许多程序,如果新版本直接废除掉fun(),会导致版本更新的异常。现在应该告诉新用户这个方法不能使用了,但是如果是已经能够正常使用的老系统,那么就照旧保留即可。。
范例:声明过期操作。
package cn.mldn.demo;
class Message{
@Deprecated
public void fun(){ //这不是覆写
System.out.println("Hello World");
}
}
public class TextDemo {
public static void main(String[] args){
new Message().fun() ;
}
}
3.压制警告信息 @SuppressWranings
警告不是语法错误,只能说明用户的操作过程中出现不当,有可能会对以后产生问题
package cn.mldn.demo;
class Message<T>{
public void fun(){ //这不是覆写
System.out.println("Hello World");
}
}
public class TextDemo {
@SuppressWarnings({"rawtypes" , "unused"})
public static void main(String[] args){
Message msg = new Message();
}
}