JAVA基础


基本数据类型

在Java中,只有基本类型不是对象(数值,字符,布尔类型).
所有的数组类型都是对象,拓展于Object类.

类型大小取值范围
int4字节-2^32~2^32-1
short2字节-2^16~2^16-1
long8字节-2^64~2^64-1
byte1字节-2^8~2^8-1
float4字节正负3.40282347E+38F(有效位6~7位)
double8字节正负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. 对本包可见—-默认

详细

作用范围当前类同一个包其他包同包子孙类不同包子孙类
publicyesyesyesyesyes
protectedyesyesnoyesyes
defaultyesyesnoyesno
privateyesnononono

继承

不同于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();
    }
}

运行结果:
结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值