JAVA基础
来源:互联网 发布:小型局域网网络搭建 编辑:程序博客网 时间:2024/05/29 17:00
基本数据类型
在Java中,只有基本类型不是对象(数值,字符,布尔类型).
所有的数组类型都是对象,拓展于Object类.
数据形式约定
- 长整型数值有一个后缀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. 对本包可见—-默认
详细
继承
不同于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(); }}
运行结果:
- Java基础01:基础
- Java基础:基础加强
- Java基础-基础
- java基础的基础
- JAVA基础---基础常识
- Java基础:基础加强
- [Java 基础]基础语法
- Java基础
- java基础
- java 基础
- java基础
- Java基础
- Java基础
- Java基础
- JAVA基础
- JAVA基础
- JAVA基础
- java基础
- C语言全局变量那些事儿
- netperf切换服务器端和客户端后,不能被侦听到。
- servlet 多线程
- 通过金矿模型介绍动态规划
- spring MessageSource
- JAVA基础
- IOS 出现linker command failed with exit code 1解决办法
- android build mm
- HDU 3537 Daizhenyang's Coin (Mock Turtles 游戏)
- eclipse不自动弹出提示
- 快速排序c++和python对比分析
- C++和双重检查锁定模式(DCLP)的风险
- poj1753
- CCTextureCache类源码分析(4)