• java基础知识-基本概念


    1.1 java语言有哪些优点?

    1.java语言为纯面向对象的语言。

    2.平台无关性。java语言的优点便是“一次编译,到处执行”。编译后的程序不会被平台所约束,因此java语言有很好的移植性。

    3.java提供了许多内置的类库,将代码封装好来给操作人员使用,从而大大减少开发人员的时间。

    4.提供了对web应用的开发的支持。

    5.具有较好的安全性和健壮性。

    6.去除了c++语言中难以理解,容易混淆的特性,例如头文件,指针,结构,单元运算符重载,虚拟基础类,多重继承等,所以java语言是由c++语言改进并重新设计而来的

    1.2 java语言和c/c++有什么异同。

    java和C++都是面向对象的语言,都使用了面向对象的思想(例如封装,继承,多态),由于面向对象有许多非常好的特性(继承,组合等),因此二者都有很好的重用性。

    下面重点说一下不同点:

    1.java为解释型语言,c/c++为编译型语言,java代码由java编译器编译成字节码,然后由JVM解释,C语言代码经过编译和链接生成可执行的二进制代码,因此java的执行速度比c++慢,但是java可跨平台执行,c/c++不能

    2.java语言没有指针

    3.java只能实现单重继承,但是可以引入多个接口

    4.java为纯面向对象语言,所有代码必须在类里实现

    5.java语言提供了垃圾回收器来实现对垃圾的自动回收,c++语言中需要开发人员去管理对内存的分配。C语言,通常会把释放资源的代码放在析构函数中,Java没有但是有finalize()方法。

    java语言不支持运算符重载,C语言支持

    java不支持默认函数参数,c语言支持

    java不提供goto语句,c/c++支持,但是在java中goto为保留关键字

    java不支持自动强制类型装换,c语言支持

    java具有平台无关性,就是对每种数据类型分配固定长度。

    java提供对注释文档的内建支持

    java包含了一些标准库

    1.3 为什么使用public static void main(String[] args)方法?

    main是程序的入口方法,所以程序执行时第一个执行的方法就是main方法。

    main()方法定义的其他几种格式:

    1.static pubic void main(String[] args)

    static 和public无先后顺序

    2.public static final void main(String[] args)

    可以定义为final

    3.static public synchronized void main(String[] args)

    可以定义为synchronized

    不管哪种定义方式,必须保证main()方法类型为void并且有static和public关键字修饰。不可以用abstract关键字,因为main()为程序的入口方法。

    1.4静态块

    静态块会在类被加载时调用,可以在main()方法前执行

    例如:

    public class jingtaikuai {

    public static void main(String[] args) {
    System.out.println("hello word");
    }
    static{
    System.out.println("静态块");
    }

    }

    执行结果:

    静态块 hello word

    1.5 java程序初始化顺序是怎样的

    java程序的初始化一般遵循三个原则(优先级依次递减):

    1.静态对象优先于非静态对象

    2.父类优先于子类

    3.按照成员变量定义顺序进行初始化

    常见面试题:

    下面代码的运行结果是什么?

    class B extends Object{
    static {
    System.out.println("load b1");
    }
    public B(){
    System.out.println("create b");
    }
    static{
    System.out.println("load b2");
    }
    }
    class A extends B{
    static {
    System.out.println("load a");
    }
    public A(){
    System.out.println("create a");
    }
    }
    public class 初始化顺序 {
    public static void main(String[] args) {
    new A();
    }

    }

    执行结果:

    load b1 load b2 load a create b create a

    1.6 java作用域

    在Java语言中,变量的类型主要有3种:成员变量、静态变量和局部变量

    首先说静态变量跟局部变量

    静态变量不依赖于特定的实例,而是被所有实例共享,也就是说,只要一个类被加载,JVM就会给类的静态变量分配

    存储空间。因此可以通过类名.变量名来访问静态变量

    局部变量的作用域与可见性为它所在的花括号内

    类的成员变量的作用范围同类的实例化对象的作用范围相同。当类被实例化的时候,成员变量就会在内存中分配空间,并初始化。

    直到类的实例化对象的生命周期结束时,成员变量的生命周期才结束。

    作用域与可见性当前类同一package子类其他package
    public
    private × × ×
    protected ×
    default × ×

    重点说一下protected和default:

    protected:表名成员变量或方法对该类自身,与它在同一个包中的其他类,在其他包中的该类的子类都可见

    default:表明该成员变量或方法只有自己和与其位于同一包内的类可见。

    若父类与子类处于同一包内,则子类对父类的default成员变量或方法都有访问权限;若父类与子类处于不同的package内,则没有访问权限


    还有需要注意的是,这些修饰符只能修饰成员变量,不能修饰局部变量。

    private和protected不能用来修饰类

    1.7 一个java文件中能否定义多个类

    一个java文件中可以定义多个类,但是最多只能有一个类被public修饰,并且这个类的类名必须和文件名相同。

    1.8 java的构造函数

    一、什么是构造函数

    java构造函数,也叫构造方法,是java中一种特殊的函数。函数名与相同,无返回值。

    作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象了属性和方法。

    在现实生活中,很多事物一出现,就天生具有某些属性和行为。比如人一出生,就有年龄、身高、体重、就会哭;汽车一出产,就有颜色、有外观、可以运行等。这些,我们就可以将这些天然的属性和行为定义在构造函数中,当new实例化对象时,也就具有这些属性和方法了,没必要再去重新定义了,从而加快了编程效率。

    构造函数是对象一建立就运行,给对象初始化,就包括属性,执行方法中的语句。

    而一般函数是对象调用才执行,用".方法名“的方式,给对象添加功能。

    一个对象建立,构造函数只运行一次。

    而一般函数可以被该对象调用多次。

     

    二、构造函数的特点

    1、函数名与类名相同

    2、不用定义返回值类型。(不同于void类型返回值,void是没有具体返回值类型;构造函数是连类型都没有)

    3、不可以写return语句。(返回值类型都没有,也就不需要return语句了

    注:一般函数不能调用构造函数,只有构造函数才能调用构造函数。

    三、示例

    1、无参构造函数中只定义了一个方法。new对象时,就调用与之对应的构造函数,执行这个方法。不必写“.方法名”。

    package javastudy;

    public class ConfunDemo {
       public static void main(String[] args) {
           Confun c1=new Confun();            //输出Hello World。new对象一建立,就会调用对应的构造函数Confun(),并执行其中的println语句。
      }
    }
    class Confun{        
       Confun(){        //定义构造函数,输出Hello World
           System.out.println("Hellow World");
      }
    }

    输出:Hellow World

     

    2、有参构造函数,在new对象时,将实参值传给private变量,相当于完成setter功能。

    package javastudy;

    public class ConfunDemo3 {
       public static void main(String[] args){
           Person z=new Person("zhangsan",3);        //实例化对象时,new Person()里直接调用Person构造函数并转转实参,相当于setter功能
           z.show();
      }
    }

    class Person{
       private String name;
       private int age;
       public Person(String n,int m){                //有参数构造函数,实现给private成员变量传参数值的功能
           name=n;
           age=m;        
      }
       //getter                                     //实例化对象时,完成了sett功能后,需要getter,获取实参值。
       public String getName(){
           return name;
      }
       public int getAget(){
           return age;
      }
       public void show(){                           //获取private值后,并打印输出
           System.out.println(name+" "+age);
      }
    }

    输出: zhangsan 3

    以上代码,我们也可以将show()方法中的输出语句直接放在构造函数中,new对象时,即可直接输出值,如下

    package javastudy;

    public class ConfunDemo3 {
       public static void main(String[] args){
           Person z=new Person("zhangsan",3);        //实例化对象时,new Person()里直接调用Person构造函数并转转实参,同时执行输出语句
      }
    }

    class Person{
       private String name;
       private int age;
       public Person(String n,int m){                //有参数构造函数,实现给private成员变量传参数值的功能,同时直接输出值
           name=n;
           age=m;
           System.out.println(name+" "+age);
      }
    }

    输出: zhangsan 3

    class ConFun
    {
       public static void main(String[] args){
           Person z=new Person(20,"zhangsan");
           System.out.println(z.getAge()+z.getName());
      }
    }

    class Person
    {
       private int age;
       private String name;
       public Person(int x,String y){
           age=x;
           name=y;
      }
       public int getAge(){
           return age;
      }
       public String getName(){
           
           return name;
      }
    }

    3、一个对象建立后,构造函数只运行一次

    如果想给对象的值再赋新的值,就要使用set和get方法,此时是当做一般函数使用

    如下:

    package javastudy;

    public class ConfunDemo4 {
       public static void main(String[] args) {
               PersonDemo s=new PersonDemo("李三",33);        //new对象时,即调用对应的构造函数,并传值。同时,不能new同一个对象多次,否则会报错。
               s.setName("李五");                            //对象建立后,想变更值时,就要用set/get方法,重新设置新的值
               s.setName("阿尔法狗");                        //并可调用对象多次。
               s.print();
      }
    }
    class PersonDemo{
       private String name;
       private int age;
       PersonDemo(String n,int m){                //建立有参构造函数,用于给两个private变量name、age赋值,同时输出值
           name=n;
           age=m;
           System.out.println("姓名:"+name+"年龄:"+age);
      }
       public void setName(String x){            //set方法,用于再次给name赋值
           name=x;        
      }
       public String getName(){                //get方法,用于获取name的赋值
           return name;
      }
       public void print(){
           System.out.println(name);
      }
    }

    输出结果:

    姓名:李三年龄:33 阿尔法狗

     

     

    四、默认构造函数

    当一个类中没有定义构造函数时,系统会给该类中加一个默认的空参数的构造函数,方便该类初始化。只是该空构造函数是隐藏不见的。

    如下,Person(){}这个默认构造函数是隐藏不显示的。

    class Person
    {  
       //Person(){}
    }

    当在该类中自定义了构造函数,默认构造函数就没有了。

    如果仍要构造函数,则需要自己在类中手动添加。

     

    五、构造函数的重载

    构造函数也是函数的一种,同样具备函数的重载(Overloding)特性。

    class Person
    {  
       private String name;
       private int age;

       Person()
      {
           System.out.println("A:name="+name+":::age="+age);
      }

       Person(String n)
      {
           name = n;
           System.out.println("B:name="+name+":::age="+age);
      }

       Person(String n,int a)
      {  
           name=n;
           age=a;
           System.out.println("C:name="+name+":::age="+age);
      }

    }

    class PersonDemo2
    {
       public static void main(String[] args)
      {
           Person p1=new Person();
           Person p2=new Person("lishi");
           Person p3=new Person("lishi",10);
      }
    }

    输出结果: A:name=null:::age=0 B:name=lishi:::age=0 C:name=lishi:::age=10

    class Person
    {  
       private String name;
       private int age;

       Person()
      {
           System.out.println("A:name="+name+":::age="+age);
           cry();
      }

       Person(String n)
      {
           name = n;
           System.out.println("B:name="+name+":::age="+age);
           cry();
      }

       Person(String n,int a)
      {  
           name=n;
           age=a;
           System.out.println("C:name="+name+":::age="+age);
           cry();

      }
       void cry()
      {
           System.out.println("Cry...............");
      }

    }

    class PersonDemo2
    {
       public static void main(String[] args)
      {
           Person p1=new Person();
           Person p2=new Person("lishi");
           Person p3=new Person("lishi",10);
      }
    }

    输出结果: A:name=null:::age=0 Cry............... B:name=lishi:::age=0 Cry............... C:name=lishi:::age=10 Cry...............

    转自:http://www.cnblogs.com/ibelieve618/p/6364541.html

    1.9 java中的clone方法

    java中所有的类都继承自Object类,这个类提供了一个clone的方法,这个方法的作用是返回一个Object对象的复制。

    使用步骤:

    1.继承Cloneable 接口

    2.重写clone()方法

    3.clone方法中调用super.clone()

    4.把浅复制的引用指向原型对象新的克隆体


    一、简单用法

    只需要在需要clone的对象上实现(implements)Cloneable接口,然后再在类中加上clone方法,在方法中只需要调用super.clone(),根据自己的需要实现即可。

    public class Student implements Cloneable {
    private String name;
    private int age;
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
    return (Student)super.clone();
    }

    public static void main(String[] args) {
    Student stu = new Student();
    stu.setAge(1);
    stu.setName("aa");
    System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
    try {
    Student sC = stu.clone();
    System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
    sC.setAge(12);
    sC.setName("bb");
    System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
    System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
    } catch (CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    }

    输出结果:

    testClone.Student@15db9742 age: 1 name: aa testClone.Student@6d06d69c sC.age: 1 sC.name: aa testClone.Student@15db9742 age: 1 name: aa testClone.Student@6d06d69c sC.age: 12 sC.name: bb

    分析结果:1、根据输出结果中前边的类名,可以得出被克隆对象的与原来的对象是同一种类型。2、根据内存地址(hashcode)知道,被克隆对象的与原来的对象是存在于内存中的不同的两个对象。所以后边有一个赋值,对原来对象没有任何影响。

    二、“影子”克隆与深度克隆

    首先看一个例子:

    class Bag{//学生的书包
    private int width;
    private String logo;
    public int getWidth() {
    return width;
    }
    public void setWidth(int width) {
    this.width = width;
    }
    public String getLogo() {
    return logo;
    }
    public void setLogo(String logo) {
    this.logo = logo;
    }
    }

    public class Student2 implements Cloneable {
    private String name;
    private int age;
    private Bag bag;

    public Bag getBag() {
    return bag;
    }
    public void setBag(Bag bag) {
    this.bag = bag;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }

    @Override
    protected Student2 clone() throws CloneNotSupportedException {
    return (Student2)super.clone();
    }

    public static void main(String[] args) {
    Student2 stu = new Student2();
    stu.setAge(1);
    stu.setName("aa");
    Bag b = new Bag();
    b.setWidth(10);
    b.setLogo("Nike");
    stu.setBag(b);
    printStudent(stu);
    try {
    Student2 sC = stu.clone();
    printStudent(sC);
    sC.setAge(12);
    sC.setName("bb");
    sC.getBag().setWidth(100);//改变书包的属性
    sC.getBag().setLogo("JNike");
    printStudent(stu);
    printStudent(sC);
    } catch (CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    /**
    * 输出
    * @param stu
    */
    private static void printStudent(Student2 stu) {
    System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +
    " bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " " +
    stu.getBag().getWidth() + ")");
    }

    } 输出结果: testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike 10) testClone.Student2@7852e922 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike 10) testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(JNike 100) testClone.Student2@7852e922 age: 12 name: bb bag: testClone.Bag@6d06d69c(JNike 100)

    分析:发现是不是跟预期的不太一样,通过第二个同学改变书包,但是第一个同学的书包也被改变了。并且通过内存地址可知,他们是同一对象(书包)。原因:调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内 容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对 象中相应的变量指向的是同一个对象。 这就是所谓的“影子”克隆。

    解决方案:深度克隆,既是对里边的引用也要克隆。以下是实现:

    class Bag implements Cloneable{//学生的书包
    private int width;//宽
    private String logo;//品牌
    public int getWidth() {
    return width;
    }
    public void setWidth(int width) {
    this.width = width;
    }
    public String getLogo() {
    return logo;
    }
    public void setLogo(String logo) {
    this.logo = logo;
    }

    @Override
    protected Bag clone() throws CloneNotSupportedException {
    return (Bag)super.clone();
    }

    }

    public class Student3 implements Cloneable {
    private String name;
    private int age;
    private Bag bag;

    public Bag getBag() {
    return bag;
    }
    public void setBag(Bag bag) {
    this.bag = bag;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }

    @Override
    protected Student3 clone() throws CloneNotSupportedException {
    Student3 stu = (Student3)super.clone();
    stu.bag = bag.clone();
    return stu;
    }

    public static void main(String[] args) {
    Student3 stu = new Student3();
    stu.setAge(1);
    stu.setName("aa");
    Bag b = new Bag();
    b.setWidth(10);
    b.setLogo("Nike");
    stu.setBag(b);
    printStudent(stu);
    try {
    Student3 sC = stu.clone();
    printStudent(sC);
    sC.setAge(12);
    sC.setName("bb");
    sC.getBag().setWidth(100);//改变书包的属性
    sC.getBag().setLogo("JNike");
    printStudent(stu);
    printStudent(sC);
    } catch (CloneNotSupportedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    /**
    * 输出
    * @param stu
    */
    private static void printStudent(Student3 stu) {
    System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +
    " bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " " +
    stu.getBag().getWidth() + ")");
    }

    }

    输出: testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike 10) testClone.Student3@7852e922 age: 1 name: aa bag: testClone.Bag@4e25154f(Nike 10) testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike 10) testClone.Student3@7852e922 age: 12 name: bb bag: testClone.Bag@4e25154f(JNike 100)

    1.10 什么是反射机制

    Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射机制。

    Class 类与java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor类(每个类都实现了Member 接口)。这些类型的对象时由JVM 在运行时创建的,用以表示未知类里对应的成员。

    这样你就可以使用Constructor 创建新的对象,用get() 和set() 方法读取和修改与Field 对象关联的字段,用invoke() 方法调用与Method 对象关联的方法。另外,还可以调用getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。

    二、获取字节码的方式

    在Java 中可以通过三种方法获取类的字节码(Class)对象

    • 通过Object 类中的getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象。

    • 所有数据类型都具备一个静态的属性.class 来获取对应的Class 对象。但是还是要明确到类,然后才能调用类中的静态成员。

    • 只要通过给定类的字符串名称就可以获取该类的字节码对象,这样做扩展性更强。通过Class.forName() 方法完成,必须要指定类的全限定名,由于前两种方法都是在知道该类的情况下获取该类的字节码对象,因此不会有异常,但是Class.forName() 方法如果写错类的路径会报 ClassNotFoundException 的异常。

      ackage com.jas.reflect;

      public class ReflectTest {
         public static void main(String[] args) {

             Fruit fruit = new Fruit();
             Class<?> class1 = fruit.getClass();     //方法一

             Class<?> class2 = Fruit.class;     //方法二

             Class class3 = null;    
             try {    //方法三,如果这里不指定类所在的包名会报 ClassNotFoundException 异常
                 class3 = Class.forName("com.jas.reflect.Fruit");
            } catch (ClassNotFoundException e) {
                 e.printStackTrace();
            }

             System.out.println(class1 + " " +class2 + "   " + class3);

        }
      }

      class Fruit{}

     

    三、通过反射机制获取类信息

    通过反射机制创建对象,在创建对象之前要获得对象的构造函数对象,通过构造函数对象创建对应类的实例。

    下面这段代码分别在运行期间创建了一个无参与有参的对象实例。由于getConstructor() 方法与newInstance() 方法抛出了很多异常(你可以通过源代码查看它们),这里就简写了直接抛出一个Exception,下同。

    复制代码

    package com.jas.reflect;

    import java.lang.reflect.Constructor;

    public class ReflectTest {
       public static void main(String[] args) throws Exception {

           Class clazz = null;
           clazz = Class.forName("com.jas.reflect.Fruit");
           Constructor<Fruit> constructor1 = clazz.getConstructor();
           Constructor<Fruit> constructor2 = clazz.getConstructor(String.class);

           Fruit fruit1 = constructor1.newInstance();
           Fruit fruit2 = constructor2.newInstance("Apple");

      }
    }

    class Fruit{
       public Fruit(){
           System.out.println("无参构造器Run...........");
      }
       public Fruit(String type){
           System.out.println("有参构造器Run..........." + type);
      }

    }

    输出: 无参构造器Run……….. 有参构造器Run………..Apple

    通过反射机制获取Class 中的属性

    package com.jas.reflect;

    import java.lang.reflect.Field;

    public class ReflectTest {
       public static void main(String[] args) throws Exception {

           Class<?> clazz = null;
           Field field = null;

           clazz = Class.forName("com.jas.reflect.Fruit");
           //field = clazz.getField("num");       getField() 方法不能获取私有的属性
           // field = clazz.getField("type");     访问私有字段时会报 NoSuchFieldException异常
           field = clazz.getDeclaredField("type");     //获取私有type 属性
           field.setAccessible(true);  //对私有字段的访问取消检查
           Fruit fruit = (Fruit) clazz.newInstance();  //创建无参对象实例
           field.set(fruit,"Apple");   //为无参对象实例属性赋值
           Object type = field.get(fruit); //通过fruit 对象获取属性值

           System.out.println(type);
      }
    }

    class Fruit{
       public int num;
       private String type;

       public Fruit(){
           System.out.println("无参构造器Run...........");
      }
       public Fruit(String type){
           System.out.println("有参构造器Run..........." + type);
      }

    }

    输出: 无参构造器Run……….. Apple

    通过反射机制获取Class 中的方法并运行。

    package com.jas.reflect;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;

    public class ReflectTest {
       public static void main(String[] args) throws Exception {

           Class clazz = null;
           Method method = null;

           clazz = Class.forName("com.jas.reflect.Fruit");
           Constructor<Fruit> fruitConstructor = clazz.getConstructor(String.class);
           Fruit fruit = fruitConstructor.newInstance("Apple");    //创建有参对象实例

           method = clazz.getMethod("show",null);  //获取空参数show 方法
           method.invoke(fruit,null);  //执行无参方法

           method = clazz.getMethod("show",int.class); //获取有参show 方法
           method.invoke(fruit,20);  //执行有参方法

      }
    }

    class Fruit{
       private String type;

       public Fruit(String type){
           this.type = type;
      }
       public void show(){
           System.out.println("Fruit type = " + type);
      }
       public void show(int num){
           System.out.println("Fruit type = " + type + ".....Fruit num = " + num);
      }
    }

    输出: Fruit type = Apple Fruit type = Apple…..Fruit num = 20

    四、反射机制简单应用(使用简单工厂创建对象)

    Class.forName() 生成的结果是在编译时不可知的,因此所有的方法特征签名信息都是在执行时被提取出来的。反射机制能过创建一个在编译期完全未知的对象,并调用该对象的方法。

    以下是反射机制与泛型的一个应用,通过一个工厂类创建不同类型的实例。

    要创建对象的实例类Apple :

    package com.jas.reflect;

    public interface Fruit {}
    class Apple implements Fruit{}

    加载的配置文件config.properties:

    Fruit=com.jas.reflect.Apple

    工厂类BasicFactory :

    package com.jas.reflect;

    import java.io.FileReader;
    import java.util.Properties;

    public class BasicFactory {
       private BasicFactory(){}

       private static BasicFactory bf = new BasicFactory();
       private static Properties pro = null;

       static{
           pro = new Properties();
           try{    
               //通过类加载器加载配置文件
               pro.load(new FileReader(BasicFactory.class.getClassLoader().
                       getResource("config.properties").getPath()));
          }catch (Exception e) {
               e.printStackTrace();
          }
      }

       public static BasicFactory getFactory(){
           return bf;
      }

       //使用泛型获得通用的对象
       public  <T> T newInstance(Class<T> clazz){
           String cName = clazz.getSimpleName();   //获得字节码对象的类名
           String clmplName = pro.getProperty(cName);   //根据字节码对象的类名通过配置文件获得类的全限定名

           try{
               return (T)Class.forName(clmplName).newInstance();   //根据类的全限定名创建实例对象
          }catch (Exception e) {
               throw new RuntimeException(e);
          }

      }
    }

    创建对象实例:

    package com.jas.reflect;

    public class ReflectTest {
       public static void main(String[] args) throws Exception {
           Fruit fruit = BasicFactory.getFactory().newInstance(Fruit.class);
           System.out.println(fruit);
      }
    }

    输出 com.jas.reflect.Apple@4554617c

    上面这个实例通过一个工厂创建不同对象的实例,通过这种方式可以降低代码的耦合度,代码得到了很大程度的扩展,以前要创建Apple 对象需要通过new 关键字创建Apple 对象,如果我们也要创建Orange 对象呢?是不是也要通过new 关键字创建实例并向上转型为Fruit ,这样做是麻烦的。

    现在我们直接有一个工厂,你只要在配置文件中配置你要创建对象的信息,你就可以创建任何类型你想要的对象,是不是简单很多了呢?可见反射机制的价值是很惊人的。

    Spring 中的 IOC 的底层实现原理就是反射机制,Spring 的容器会帮我们创建实例,该容器中使用的方法就是反射,通过解析xml文件,获取到id属性和class属性里面的内容,利用反射原理创建配置文件里类的实例对象,存入到Spring的bean容器中。

    参考书籍: 《Java 编程思想》 Bruce Eckel 著 陈昊鹏 译

    1.10 java创建对象的几种方式

    1. 通过new实例化一个对象

    2. 通过反射机制创建对象

    3. 通过clone方法创建一个对象

    4. 通过反序列化方式创建一个对象

    1.11 package作用

    package的作用

    • package的中文意思是“包”,它是一个比较抽象的逻辑概念,其宗旨是把.java文件 (Java源文件)、.class文件(编译后的文件)以及其他resource文件(例如.xml文件、.avi文件、.mp3文件、.txt文件等)有条理地进行一个组织,以供使用。它类似于Linux文件系统, 有一个根,从根开始有目录和文件,然后目录中嵌套目录。

    • 具体而言,package主要有以下两个作用: 第一,提供多层命名空间,解决命名冲突,通过使用package,使得处于不同package中的类可以存在相同的名字。 第二,对类按功能进行分类,使项目的组织更加清晰。当开发一个有非常多的类的项目时,如果不使用package对类进行分类,而是把所有类都放在一个package下,这样的代码不仅可读性差,而且可维护性也不好,会严重影响开发效率。

    2)package的用法

    • package的用法一般如下(源文件所在目录为当前目录): 1.在每个源文件的开头加上"package packagename;",然后源文件所在目录下创建一个新目录,名称为 packapename。 2.用javac指令编译每个sourcename. java源文件,将生成的sourcename. classname文件复制到packagename 目录。 3.用 java 指令运行程序:java packagename. sourcename。

    3)实例 以下是一个简单的程序示例。

    package top.itcourse.pkg;

    public class TestPackage {
       public static void main(String[] args) {
           System.out.println("Hello World!");
      }
    }

    结果: Hello World!



  • 相关阅读:
    PHP实现栈(Stack)数据结构
    为什么推荐std::string而不是char*
    PHP实现插入排序算法
    OpenCms Application dev-ref
    OpenCMS integration with Spring MVC--reference
    安装opencms时遇到问题及解决方法
    Java ZIP File Example---refernce
    Top 10 Algorithms for Coding Interview--reference
    JVMInternals--reference
    java code to byte code--partone--reference
  • 原文地址:https://www.cnblogs.com/dmzna/p/11830640.html
Copyright © 2020-2023  润新知