基础概念

2016-10-18  本文已影响0人  A_Coder

1.基本概念


2.为什么需要public static void main(String []args)这个方法
      public static void main(String []args)为Java程序的入口方法,JVM在运行程序时,会首先查找main()方法。其中,public是权限修饰符,表明任何类或对象都可以访问这个方法,static表明这个main()方法是一个静态方法,即方法中的代码是存储在静态存储区的,只要类被加载后,就可以使用该方法而不需要通过实例化对象来访问,可以直接通过类名.main()直接访问,JVM在启动时就是按照上述方法的签名(必须有public和static修饰,返回值为void,且方法的参数为字符串数组)来查找方法的入口地址,若能找到,就执行;找不到就会报错。void表明该方法没有返回值,main是JVM识别的特殊方法名,是程序的入口方法。字符串数组参数args为开发人员在命令行状态下与程序交互提供了一种手段。
注:


3.如何实现在main()方法执行前输出“Hello World”
      众所周知,在Java语言中,main()方法是程序的入口方法,在程序运行时,最先加载的就是main()方法,但main()方法不一定是程序运行时第一个被执行的模块。
在Java语言中,由于静态块在类被加载时就会被调用,因此可以在main()方法执行前,领用静态块实现输出“Hello World”的功能,如下代码:

public class Test {
static {
    System.out.println("Hello World1");
}
public static void main(String args[]){
    System.out.println("Hello World2");
}
}

输出结果是:
Hello World1
Hello World2

      由于静态代码不管顺序如何,都会在main()方法执行之前执行,因此一下代码与上面的代码有同样的输出结果:

public class Test {

public static void main(String args[]){
    System.out.println("Hello World2");
}
static {
    System.out.println("Hello World1");
}
}
```
***
**4.Java程序初始化的顺序是怎样的**
      在Java语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员都实例化后,才会调用对象所在类的构造函数创建对象。
      在Java程序的初始化一般遵循3个原则(优先级依次递减):1.静态对象(变量)优先于非静态对象(变量)初始化,其中,静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化很多次。2.父类有限元子类进行初始化。3.按照成员变量的定义顺序进行初始化。即使变量散布在方法定义之中,他们依然在任何方法(包括构造函数)被调用前先初始化。
      Java程序初始化工作可以在许多不同的代码块中来完成(例如静态代码块、构造函数等),他们的执行顺序如下:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
***
**5.Java作用域有哪些**
      在Java语言中,作用域是由花括号的位置决定的,它决定了其定义的变量名的可见性与生命周期。
      在Java语言中,有3种变量的类型:成员变量、静态变量和局部变量。类的成员变量的作用范围与类的实例化对象的作用范围相同,当类被实例化时,成员变量就会在内存中分配空间和初始化,直到这个类的对象的生命周期结束时,成员变量的生命周期才结束。被static修饰的成员变量称为静态变量或全局变量,与成员变量不同的是,静态变量不依赖于特定的实例,而是被所有实例所共享,也就是说,只要一个类被加载,JVM就会给类的静态变量分配存储空间。因此,可以通过类名和变量名来访问静态变量。
      此外,成员变量也有4种作用域:public、private、protected、default。
- public:表明该成员变量或方法对所有类或对象都是可见的,所有类或对象都可以直接访问。
- private:表明该成员变量或方法是私有的,只有当前类对其具有访问权限,除此之外的其他类或对象都没有访问权限。
- protected:表明该成员变量或方法对自己及其子类都是可见,即自己和子类具有权限访问。除此之外的其他类或对象都是没有访问权限。
- default:表明该成员变量或方法只有自己或其位于同一个包内的类可见。若父类与子类位于同一个包内,则子类对父类的default成员变量或方法有访问权限;若父类与子类位于不同的package内,则没有访问权限。
**注:**
      这些修饰符只能修饰成员变量或方法,不能用了修饰局部变量。private与protected不能用来修饰类(只有public、abstract或final能用来修饰类)。
***
**6.一个Java文件中是否可以定义多个类**
      一个Java文件中可以定义多个类,但是最多只能有一个类被public修饰,并且这个类的文件与文件名必须相同,若有这个文件中没有public的类,则文件名随便是一个类的名字即可。
***
**7.构造函数**
      构造函数是一种特殊的函数,用来在对象实例化时初始化对象的成员变量。构造函数具有一下特点:
- 构造函数必须与类的名字相同,并且不能有返回值。
- 每个类可以有多个构造函数。如果没写构造函数,编译器在编译源代码时提供一个无参数的默认构造函数。
- 构造函数可以有0个,1个或以上的参数。
- 构造函数总是伴随着new操作一起调用,且不能由程序的编写者直接调用,有系统调用。构造函数在对象实例化时会被自动调用,且只运行一次;而普通的方法是在程序执行到它时调用,且可以被对象调用多次。
- 构造函数的主要作用是完成对象的初始化工作。
- 构造函数不能被继承,因此,它不能被覆盖,但是构造函数可以被重载,可以使用不同的参数个数或参数类型来定义多个构造函数。
- 子类可以通过super关键字来显式地调用父类的构造函数,当父类没有提供无参数的额构造函数时,子类的构造函数中必须显式地调用父类的构造函数。如果父类提供了无参数的构造函数,此时子类的构造函数就可以不显示的调用父类的构造函数,在这种情况下编译器会默认调用父类提供的无参数的构造函数,当有父类时,在实例化对象时会先执行父类的构造函数,然后执行子类的构造函数。
- 当父类和子类都没有定义构造函数时,编译器会为父类生成一个默认的无参数的构造函数,给子类也生成一个默认的无参数的构造函数。此外,默认构造器的修饰符只跟当前类的修饰符有关,例如,如果一个类被定义为public,那么它的构造函数也是public。
**注:普通方法可以与构造函数同名,但是实例化对象时就运行构造函数**

***
**8.为什么Java中有些接口没有任何方法**
      接口是抽象方法定义的集合(接口中也可以定义一些常量值),是一种特殊的抽象类,接口中只包含方法的定义,没有方法的实现。接口中的所有方法都是抽象的。接口中成员的作用域修饰符都是public,接口中的常量值默认使用public static final修饰。由于一个类可以实现多个接口,因此通常可以采用实现多个接口的方式来间接达到多重继承的目的。
      在Java语言中,有些接口内部没有声明任何方法,也就是说,实现这些接口的类不需要重写任何方法,这些没有任何方法声明的接口又被称为**标识接口**,**标识接口对实现它的类没有任何语义上的要求,他仅仅充当一个标识的作用,用来表明实现它的类属于一个特定的类型**。这个标签类似于汽车的标志图标,每当人们看到一个汽车车标时,就能知道这款汽车的品牌。Java类库中已存在的标识接口有Cloneable和Serializable等。在使用时会经常用instanceof来判断实例对象的类型是否实现了一个给定的标识接口。
***
**9.Java中clone方法有什么用**
      实质上每个new语句返回的都是一个指针的引用,只不过在大部分情况下开发人员不需要关心如何去操作这个指针而已。
      由于Java取消指针的概念,因此开发人员在编程中往往忽略对象和引用的区别。在Java中,处理基本数据类型(如int,char,double等)时,都是采用按值传递(传递的是输入参数的复制)的方式执行,除此之外的其他类型都是按引用传递(传递的是对象的一个引用)的方式执行。对象除了在函数调用时是引用传递,在使用“=”赋值时也是采用引用传递,(如:Object A = new Object();   Object A = new Object();   b = a;)
      在实际编程中,经常会遇到从某个已有的对象A创建出另外一个与A具有相同状态的对象B,并且对B的修改不会影响到A的情况,如Prototype(原型)模式中,就需要clone一个对象实例。在Java语言中,仅仅通过简单的赋值操作显然无法达到这个目的,而Java提供了一个简单有效的clone()方法来满足需求。
      Java中的所有类默认都继承自Object类,而Object类中提供了一个clone()方法。这个方法的作用是返回一个Object对象的复制。这个复制函数返回的是一个新的对象而不是也给引用。使用步骤:
- 实现clone的类首先需要继承Cloneable接口。Cloneable接口实质上是一个标识接口,没有任何接口方法;
- 在类中重写Object类中的clone()方法;
- 在clone方法中调用super.clone()。调用java.lang.Object类的clone()方法;
- 把浅复制的引用指向原型对象的新的克隆体。

      Java语言在重载clone()方法时,当开发人员自定义复制构造函数时,也会存在浅复制和深复制之分,当类中只有一些基本的数据类型时,上述步骤就行。但是当类中包含了一些对象时,就需要用到深复制了,实现方法是在对对象调用clone()方法完成复制后,接着对对象中的非基本类型的属性也调用clone()方法完成深复制。
***
**10.反射机制**
      它允许程序在运行时进行自我检查,同时也允许对其内部成员进行操作。由于反射机制能够实现在运行时对类进行装载,因此能增强程序的灵活性,但是不恰当地使用反射机制,也会严重影响系统的性能。
反射机制的提供的功能主要有:
- 得到一个对象所属的类;
- 获取一个类的所有成员变量和方法;
- 在运行时创建对象;
- 在运行时调用对象的方法。

      反射机制非常重要的一个作用就是可以在运行时动态地创建类的对象,如下:
```
class Base{
    public void f() {
        System.out.println("Base");
    }
}
class Sub extends Base {
    public void f() {
        System.out.println("Sub");
    }
}
public class Test {
    public static void main(String []args) {
      try{ //使用反射机制加载类
        Class c = Class.forName("Sub");
        Base b = (Base) c.newInstance();
        b.f();
      }catch(Exception e){
          e.printStackTrace();
      }
    }
}
```
程序运行结果为:
Sub

在反射机制中,class是一个非常重要的类,有3种方法可以获取到类:
- class.forName("类的路径"),如上例;
- 类名.class;
- 实例.getClass();

Java创建对象的方式有4种:
- 通过new语句实例化一个对象;
- 通过反射机制创建对象,如上例;
- 通过clone()方法创建一个对象;
- 通过反序列化的方式创建对象;

***
**11.回调函数**
      回调函数,就是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Java中没有只针对的概念,可以利用接口与类来实现类似于函数指针的功能。首先定义一个接口,然后在接口中声明要调用的方法,接口实现这个接口,最后把这个实现类的一个对象作为参数传递给调用程序,调用程序通过这个参数来调用指定的函数,从而实现回调函数的功能,如下:
```
//接口中定义了一个用来比较大小的方法
interface IntCompare{
    public int cmp(int a, int b);
}
class Cmp1 implements IntCompare{
    public int cmp(int a, int b) {
        if(a > b) {
            return 1;
        } else if(a < b) {
            return -1;
        } else 
            return 0;
    }
}
class Cmp2 implements IntCompare{
    public int cmp(int a, int b) {
        if(a < b) {
            return 1;
        } else if(a >b) {
            return -1;
        } else 
            return 0;
    }
}
public clas Test {
    public static void insertSort(int []a, IntCompare cmp) {
        if(a != null) {
            for(int  i = 1; i < a.length; i++ ) {
                int temp = a[i],j=i;
                if(cmp.cmp(a[j-1], temp) == 1) {
                    while(j >= 1 && cmp.cmp(a[j-1], temp) == 1) {
                        a[j] = a[j-1];
                        j--;
                    } 
                }
                a[j] = temp;
            }
        }
    }
    public static void main(String []args) {
        int []array1 = {7,3,29,40,4,7,1};
        insertSort(array1, new Cmp1());
        System.out.println("升序排列:");
        for(int i = 0;i<array1.length;i++){
            System.out.println(array1[i]+"");  
        }
        System.out.println();
         int []array2 = {7,3,29,40,4,7,1};
        insertSort(array2, new Cmp2());
        System.out.println("降序排列:");
        for(int i = 0;i<array1.length;i++){
            System.out.println(array2[i]+"");  
        }
    }
}
```
程序运行结果:
升序排列:1 3 4 7 7 29 40 
降序排列: 40 29 7 7 4 3 2 1

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定义了一个比较大小的接口,在使用时,开发人员可以根据实际需要传入自定义的类,这也是策略设计模式所用到的思想。

***
**参考:Java程序员面试笔试宝典------何昊**
上一篇下一篇

猜你喜欢

热点阅读