基本数据类型
在Java中,只有基本类型不是对象(数值,字符,布尔类型).
所有的数组类型都是对象,拓展于Object类.
类型 | 大小 | 取值范围 |
---|---|---|
int | 4字节 | -2^32~2^32-1 |
short | 2字节 | -2^16~2^16-1 |
long | 8字节 | -2^64~2^64-1 |
byte | 1字节 | -2^8~2^8-1 |
float | 4字节 | 正负3.40282347E+38F(有效位6~7位) |
double | 8字节 | 正负1.7976313486231570E+308(有效位15位) |
数据形式约定
- 长整型数值有一个后缀L(如4000000000L).
- 十六进制数值有前缀0x,八进制前缀为0.
- float类型的数值有一个后缀F(如3.402F),没有后缀F的浮点数默认为double类型.double类型数值后缀为D.
对象包装器与自动打包
所有基本类型都有一个与之对应的类,这些类称为包装器.
Integer,Long,Float,Double,Short,Byte,Character,Void,Boolean.对象包装器是不可变的,即一旦构造了包装器就不允许改变包装在其中的值.
ArrayList<Integer> list=new ArrayList<Integer>();//数组列表尖括号中的类型参数不允许是基本类型
list.add(3);//自动变换成list.add(new Integer(3));这种变换称为自动打包.
int n=list.get(0);//自动变换成int n=list.get(0).intValue();称为自动拆包
枚举类型
enum Size{SMALL,MEDIUM,LARGE};
Size s=Size.SMALL;
字符串
int compareTo(String other)
按照字典排序,如果字符串位于other之前,返回负数;如果位于之后返回正数;相等返回0.boolean equals(Object other)
如果字符串与other相等返回true.int indexOf(String str)
返回与str匹配的第一个子串的开始位置;若不存在,返回-1.String substring(int begin)
String substring(int begin,int end)
返回字符串中从begin到串尾或end-1的子串String replace(String old,String new)
将new替换掉字符串中所有的old.String trim()
去除字符串头部和尾部的空格
构建字符串
StringBuilder builder=new StringBuilder();
builder.append('a');
builder.append('b');
builder.append("cdef");
String completeString=builder.toString();
输入输出
控制台输入输出
Scanner in=new Scanner(System.in);
System.out.print("What is your name?");
String name=in.nextLine();
文件输入输出
Scanner in=new Scanner(new File("infile.txt"));
String s=in.nextLine();
PrintWriter out=new PrintWriter("outfile.txt");
out.printf("%s",s);
输入函数
String nextLine()
读取下一行
String next()
读取下一个单词(以空格作为分隔符)
int nextInt()
double nextDouble()
boolean hasNext()
检测是否还有其他单词
boolean hasNextInt()
boolean hasNextDouble()
数组
For each循环
for(variable : collection) statement
collection这一集合表达式必须是一个数组或者实现了Iterable接口的类对象.
数组拷贝
在JAVA中,允许将一个数组变量拷贝给另一个数组变量,这时两个变量将引用同一个数组
int[] newNums=oldNums;
newNums[5]=12;//oldNums[5]也将变成12
将一个数组的所有值拷贝到另一个新的数组中可避免上面的问题
int[] newNumbers=Arrays.copyOf(oldNumbers,oldNumbers.length);
//如果新老数组是同一个数组则可通过这个方法增大数组大小
数组排序
Arrays类中的sort方法承诺可以对对象数组排序,但要求满足下列前提:对象所属的类必须实现了Comparable接口
int[] a=new int[100];
....
Arrays.sort(a);
数组赋初始值
static void fill(type[] a,type value)
将数组所有元素值设为value.
数组equals
static Boolean equals(type[] a,type[b])
如果两个数组长度且元素完全一样则返回true.数组类型可以是:Object;int,long,short,char,byte,boolean,float,double.
泛型数组
ArrayList是保存对象类型元素的泛型类
boolean add(T obj)
数组列表尾端添加一个元素。永远返回true.
int size()
返回实际存储的元素个数
void trimToSize()
去除列表多余的空间
void set(int index,T obj)
设置第index个元素
T get(int index)
返回第index个元素
void add(int index,T obj)
在index+1的位置上插入元素
T remove(int index)
删除并返回第index个元素
产生随机数
Random generator=new Random();
int x=generator.nextInt(1000);//返回0~999之间的随机数
对象与类
- 如果将一个方法应用于一个值为null的对象上会产生运行错误.
- 变量不会自动地初始化为null,必须通过调用new或显式的设置为null.
- 文件名必须与public类的名字一样.在一个源文件中只有一个public类,可以有任意个非public类.,非public类的访问权限为默认—-包可见.
方法参数
值调用
表示方法接受的是调用者提供的值;引用调用
表示方法接受的是调用者提供的变量地址. Java总是采用值调用
.
- 一个方法不能修改一个基本数据类型的参数(数值型和布尔型)
- 一个方法可以改变一个对象参数的状态
- 一个方法不能实现让对象参数引用一个新的对象
访问权限
概略
1. 仅对本类可见—-private
2. 对所有类可见—-public
3. 对本包和所有子类可见—-protected
4. 对本包可见—-默认
详细
作用范围 | 当前类 | 同一个包 | 其他包 | 同包子孙类 | 不同包子孙类 |
---|---|---|---|---|---|
public | yes | yes | yes | yes | yes |
protected | yes | yes | no | yes | yes |
default | yes | yes | no | yes | no |
private | yes | no | no | no | no |
继承
不同于C++,Java不支持多继承
- 使用super调用构造器的语句必须是子类构造器的第一条语句;如果子类的构造器没有显式的调用超类的构造器,则自动调用超类默认的构造器(即没有参数的构造器),如果超类不存在无参数构造器则出错.
- 不允许继承的类称为final类
final class Manager extends Employee
{
...
}
类中的方法也可以声明为final,子类就不能覆盖这个方法
class Employee
{
...
public final String getName()
{
return name;
}
...
}
- 将子类的引用赋给一个超类变量是允许的;将超类的引用赋给子类变量需要进行类型转换. 在超类转换成子类之前应该使用instanceof进行检查.
if(staff[1] instanceof Manager)
{
boss=(Manager) staff[1];
...
}
抽象类
包含一个或多个抽象方法的类本身必须被声明为抽象类。
除了抽象方法之外,抽象类还可以包含具体数据和具体方法
- 类即使不含抽象方法,但可以声明为abstract类
- abstract不能被实例化
abstract class Person
{
...
public Person(String n)
{
name=n;
}
public abstract String getDescription();
private String name;
}
相等测试与继承
instanceof
子类对象instanceof父类为true
Object类中的equals方法用于检测两个对象是否具有相同的引用
//A是超类,B是子类
Object a=new A();
Object b=new B();
a instanceof A //true
a instanceof B //false
b instanceof A //true <----------
b instanceof B //true
a.getClass().equals(A.class) // true
a.getClass().equals(B.class) // false
b.getClass().equals(A.class) // false <----------
b.getClass().equals(B.class) // true
完美的equals方法
class B extends A
{
...
@Override
public boolean equals(Object otherObject)
{
//检测this与otherObject是否引用同一个对象
if(this==otherObject) return true;
//如果为空
if(otherObject==null) return false;
如果子类拥有自己相等的概念,需要采用getClass检测
{
if(getClass()!=otherObject.getClass()) return false;
B other=(B) otherObject;
return field1=other.field&&field2==other.field2...;
}
如果由超类决定相等的概念,则超类肯定定义了equals方法
使用instanceof检测
{
if(!(otherObject instanceof A)) return false;
return super.equals(otherObject);
}
}
...
}
Object类hashCode,toString方法
- Object类中的hashCode方法,其值为对象的存储地址.
- Object类中的toString类用来打印对象的类名和散列码
- equals与hashCode的定义必须一致:如果x.equals(y)==true,那么x.hashCode()就必须与y.hashCode()具有相同的值。例如,如果用定义的Employee.equals比较雇员ID,那么hashCode方法就需要散列ID,而不是姓名或薪水.
参数数量可变的方法
用户可以定义参数数量可变的方法,并将参数指定为任意类型,甚至是基本类型.
public double max(double... values)
{
...
values[i]
...
}
可以这样调用上面的方法
double m=max(new double[]{3.1,4.3,5});
//或者使用下面方法,使用了自动打包
double m=max(3.1,4.3,5);
反射
能够分析类能力的程序被称为反射.
Class类
一个Class对象实际表示的是一个类型,而这个类型未必是一种类.
获取Class类对象的三种方法
- 通过对象获取对应的Class类
Employee e;
...
Class cl=e.getClass();
- 调用静态方法Class.forName(类名)获得类名对应的Class类
String className="java.util.Date";
Class cl=Class.forName(className);
- 如果T是任意Java类型,T.class就是T对应的Class类型
Class cl1=Date.class;
Class cl2=int.class;
通过Class类可以快速的创建一个类的实例
cl.newInstance();//调用默认的构造器,如果不存在会抛出异常
利用反射分析类的能力
Class类中的getFields,getMethods,getConstructors方法将分别返回类的public(和本包的protect)域,方法和构造器,其中包括超类的public成员.
Class类中的getDeclareFields,getDeclareMethods,getDeclareConstructors方法将分别返回类的全部域,方法和构造器,其中包括private和protected成员,但不包括超类的成员.
String modifiers=Modifier.toString(cl.getModifiers());
获取修饰符public/private/protect+[static]+[final]
- 获取所有方法
Method[] methods=cl.getDeclaredMethods();
for(Method m:methods)
{
String modifiers=Modifier.toString( m.getModifiers()); //获取修饰符
Class reType=m.getReturnType();
String name=reType.getName();//获取方法返回值的类型
//获取方法名
String name=m.getName();
//获取参数
Class[] paramTypes=m.getParameterTypes();
for(int j=0;j<paramTypes.length;++j)
{
System.out.print(paramTypes[j].getName());
}
}
- 获取所有构造器
Constructor[] constructors=
cl.getDeclaredConstructors();
for(Constructor c:constructors)
{
String modifiers=Modifier.toString( c.getModifiers()); //获取修饰符
//获取构造器名
String name=c.getName();
//获取参数
Class[] paramTypes=c.getParameterTypes();
for(int j=0;j<paramTypes.length;++j)
{
System.out.print(paramTypes[j].getName());
}
}
- 获取所有域
Field[] fields=
cl.getDeclaredFields();
for(Field f:fields)
{
String modifiers=Modifier.toString( f.getModifiers()); //获取修饰符
//获取域的类型名
Class type=f.getType();
String typeName=type.getName();
//获取域名
String name=f.getName();
System.out.print(modifiers+" "+typeName+" "+name);
}
在运行时使用反射分析对象
利用反射机制可以查看在编译时还不清楚的对象域.
获取对象域的值
Employee harry=new Employee("Harry Hacker",35000,
10,1,1995);
Class cl=harry.getClass();
Field f=cl.getDeclaredField("name");//通过域名获取指定域
Object v=f.get(harry);//获取harry对象的name域的值
当域是私有域,get方法会抛出IllegalAccessException. 为了达到目的,需要调用Field、Method或Constructor对象的setAccessible
方法.
Field f=cl.getDeclaredField("name");
f.setAccessible(true);
Object v=f.get(harry);
f.set(obj,value)
将obj对象的f域设置为value.AccessibleObject.setAccessible(fields,true)
设置fields数组get权限.
使用反射编写泛型数组代码
bad猜想代码
void fun()
{
Employee[] a=new Employee[100];
...
//a 满了
a=(Employee[]) badArrayGrow(a);
}
Object[] badArrayGrow(Object[] a)
{
int newLength=a.length*2;//数组长度增加一倍
Object[] newArray=new Object[newLength];
System.arraycopy(a,0,newArray,0,a.length);
return newArray;
}
上面的代码有什么问题?
- 一个一开始就是Object类型的对象是不可以转成Employee对象的.
Object[] newArray
不能转成Employee[]
. - 即使第一个问题解决了,也不能处理好基本类型数组的拓展。比如执行下面的代码会出问题:
double[] a=new double[10];
a=(double[]) badArrayGrow(a);
good猜想代码
- 基于
Array.newInstance(componentType,newLength)
方法生成新的数组
Core Java(第八版)的代码
Object goodArrayGrow(Object a)
{
Class cl=a.getClass();
if(!cl.isArray()) return null;
Class componentType=cl.getComponentType();
int newLength=a.length*2;//数组长度增加一倍
Object[] newArray=Array.newInstance( componentType,newLength);
System.arraycopy(a,0,newArray,0,a.length);
return newArray;
}
方法指针(invoke)
从表面上看,Java没有提供方法指针,即将一个方法的存储地址传给另外一个方法,以便第二个方法能够随后调用它. Java提供的接口是一种更好的解决方案,因为接口会使得代码执行速度更快,但Java中方法指针已经作为反射包的副产品出现了.
invoke的参数和返回值必须是Object类型,如果是基本类型,会自动打包拆包.
通过方法名获取方法:
Method m1=Employee.class.getMethod("getName");//获取Employee.class的getName方法
如果需要获取的方法有参数,则还要带入参数:
Method m2=Employee.class.getMethod("raiseSalary",double.class);//获取Employee.class的raiseSalary方法
下面的语句将显示如何调用某个对象的方法:
String n=(String) m1.invoke(harry);//执行harry对象的getName方法
m2.invoke(harry,100);//执行harry对象的raiseSalary方法
如果ml方法为静态方法,调用静态方法:
ml.invoke(null [,param]);
接口与内部类
一个类可以实现一个或多个接口,并在需要接口的地方随时使用实现了相应接口的对象.
- 接口绝不能含有实例域,也不能在接口中实现方法.
- 接口可以包含常量,接口中的域被自动设为public static final.
- 接口中所有方法自动地属于public. 在接口中声明方法不必提供关键字public. 在实现接口时必须把方法声明为public. 继承方法的时候应该放宽或者保持访问权限.
可以使用instanceof
检测一个对象是否实现了某个接口.if(obj instanceof Comparable) {...};
接口继承
public interface Powered extends Comparable
{
double fun();
}
实现接口
class Employee implements Cloneable[,others]
{
...
}
接口与抽象类?
每个类只能拓展于一个类,却可以实现多个接口.
对象克隆
Java里对象之间等号赋值实现的只是引用,要实现复制一个对象要通过克隆.
- Object类实现的clone方法只能将数值或者基本类型拷贝,拷贝的子对象其实还是引用的同一个子对象.
Object类的clone方法
clone是Object类的protected方法,用户在编写的代码不能直接调用它,也就是无法直接调用anObject.clone()??—–Core Java第八版
这是我做的测试:
一:
public class A{
public static void main(String... args) throws CloneNotSupportedException
{
new B().clone(); //编译不通过 The method clone() from the type Object is not visible
}
}
class B{
}
- 编译不通过的原因是,在不同包的子类内部才可以访问超类的protected域和方法.B子类在A子类内部不能访问超类Object的clone方法.
二:
public class A implements Cloneable{
public static void main(String... args) throws CloneNotSupportedException
{
new A().clone(); //运行成功
}
}
class B{
}
总结:其实Core Java(第八版)的这句话说得不合适,它应该这么说:clone是Object类的protected方法,只有在用户编写的Object子类内部才可以调用超类(也就是Object)的protected方法,其他情况不能直接调用它. 即使clone默认的实现能够满足需求,也应该实现Cloneable接口(或者会抛出异常),将clone重定义为public,并调用super.clone().
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
Employee cloned=(Employee)super.clone(); //浅拷贝
cloned.hireDay=(Date) hireDay.clone(); //深拷贝
return cloned;
}
}
值得注意的是,Cloneable接口并没有指定clone方法,Cloneable只是作为一个标记,表明类设计者知道要进行克隆处理.
内部类
内部类是定义在另一个类中的类.
类中的类
- 内部类既可以访问自身的域,也可以访问外围类对象的域.
public class A
{
private int interval;
public void fun(){/*...;*/}
class B
{
interval=...;
}
}
- 如果内部类是一个public类,则可以根据任意的外围类对象创建内部类对象.
public class A
{
/*...;*/
public class B
{
}
}
对于上面的代码:
A a = new A();
A.B ab = a.new B();//创建内部类对象,相当于ab的类是A.B
内部类是一种编译器现象,与虚拟机无关. 编译器会把内部类翻译成用$分隔外部类名与内部类名的常规类文件. 可通过反射查看.
只有内部类可以声明为static,在内部类不需要访问外围类对象的时候应该使用静态内部类.
方法中的类
- 方法中的类(即局部内部类)不能用public,protected,private访问说明符进行声明. 它的作用域被限定在这个方法中,对外部世界完全的隐藏.
- 若方法中的类用到了方法里的变量,这些变量必须声明为final.
public class A
{
/*...;*/
public void Afun(int n,final String s) //参数n,s 也属于方法的变量
{
n;
class B
{
//因为内部类里面用到了外部方法的变量,编译器会在内部类生成final String val$s变量,所以外部方法的变量要为final
public void Bfun()
{
s;
/*...;*/
}
}
}
}
代理
利用代理可以在运行时创建一个实现了一组给定接口的新类.
这种功能只有在编译时无法确定需要实现哪些接口是才有必要使用.
使用代理的原因:
- 路由对远程服务器的方法调用(如Hessian).
- 在程序运行期间,将用户接口事件与动作关联起来.
- 为调试,跟踪方法调用.
要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法. 这个方法有三个参数.
- 类加载器,用null表示使用默认的类加载器.
- Class对象数组,调用处理器构造时的参数对象需要实现的接口.表示的是我给需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了.
- 调用处理器
调用处理器
是实现了InvocationHandler接口的类对象,在这个接口中只有一个方法:Object invoke(Object proxy,Method method,Object[] args)
.无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用. 实际对象实现实际功能.
下面的代码来自:Ruthless
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface HelloWorld {
public void sayHelloWorld();
}
//类HelloWorldImpl是HelloWorld接口的实现
class HelloWorldImpl implements HelloWorld{
public void sayHelloWorld() {
System.out.println("HelloWorld!");
}
}
class HelloWorldHandler implements InvocationHandler{
//要代理的原始对象
private Object obj;
public HelloWorldHandler(Object obj) {
super();
this.obj = obj;
}
/**
* 在代理实例上处理方法调用并返回结果
*
* @param proxy 代理类
* @param method 被代理的方法
* @param args 该方法的参数数组
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
//调用之前
doBefore();
//调用原始对象的方法
result=method.invoke(obj, args);
//调用之后
doAfter();
return result;
}
private void doBefore(){
System.out.println("before method invoke");
}
private void doAfter(){
System.out.println("after method invoke");
}
}
public class HelloWorldTest {
public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorldImpl();
InvocationHandler handler=new HelloWorldHandler(helloWorld);
//创建动态代理对象
HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(
HelloWorldTest.class.getClassLoader(),
helloWorld.getClass().getInterfaces(),
handler);
proxy.sayHelloWorld();
}
}
运行结果: