java core学习,记录看到的需要注意的

java中不能使用==检测两个字符串是否相等,这个运算符只能确定两个字符串是否放在同一个位置上。

java标准输出:System.out.println

java中不能在嵌套的两个块中声明同名的变量,如:

public static void main(String[] args)
{
    int n;
    ...
    {
        int k;
        int n;  //错误,不能声明n
    }
}
以上会出现错误,但是在C++中却是正确的。

java数组:

可以初始化一个匿名的数组:new int[ ] {17, 19, 23, 29, 31, 37}; 使用这种语法可以在不创建新变量的情况下重新初始化一个数组,如:

smallPrimes = new int[] {17, 29, 23, 32, 45};
是以下形式的简写:
int[] anonymous = {17, 29, 23, 32, 45};
smallPrimes = anonymous;
java允许数组的长度为0,可以创建长度为0的数组,new elementType[0],但这与null是不同的。

java数组可以拷贝,如果将一个数组变量赋值给另一个数组变量,则两个变量指向同一个数组,如果要拷贝数组元素,则用Arrays的copyOf方法:

int lucyNumbers = smallPrimes;

int[] copiedLucyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

第二个参数是新数组的长度,可以超过原拷贝数组的长度,这样可以增加新数组的长度,多余的部分将赋值为0,如果少于拷贝数组的长度,则只拷贝指定的值。

java的数组是分布在堆上的,例如java初始化一个数组:int[] a = new int[100],是等同于C++的int* a= new int[100];

java的main方法中args参数并没有存储程序名,第一个放的就是程序命令行的第一个参数。

java文件中只能有一个共有类,但可以有任意书目的非共有类,且文件名必须与public类的名字相同。


java类:

方法的签名指方法名与参数类型,不包括返回值,即返回值不是方法签名的一部分,也就是不能有两个名字相同,参数也相同,返回值不同类型的方法。

如果在构造器中没有显示地给域赋予初值,那么会被自动赋为默认值:数值为0,布尔值为false,对象引用初始化为null,这是域与局部变量的不同,方法中的局部变量必须赋初值,但是如果没有初始化类中的域,则会被初始化为默认值。

同时这也是与C++不同的地方,C++类中当类中的变量没有赋予初始值时,如果变量是基本类型,则是任意值,如果是类类型,则常识调用类的构造函数,如果有默认构造函数,则初始化,没有则不初始化。

C++中构造器不能调用另一个构造器,但是java中可以,例如:

//构造器中调用另一个构造器,当new Employee(6000)时构造器
//将调用Employee(String, double)构造器
public Employee(double s)
{
    this("Employee #"+nextId, s);
    nextId++;
}

继承:

java用extend关键字替代C++的:继承,且java中只有共有继承,没有私有继承和保护继承。java中不存在多继承。

java中使用final关键字可以阻止子类继承超类,在C++中如果要实现这样的功能需要采用虚基类,这样虚基类中所有方法都必须在子类中重写,如果父类不是虚基类,某个函数不希望子类默认继承,则也将该方法写为纯虚函数,则可以实现子类不能继承这个实现,必须要自己重新定义这个函数。

java中控制可见性的修饰符:

1)private:仅对本类可见

2)public:对所有类可见

3)protected:对本包和所有子类可见

4)默认,什么都都不写,对本包可见,不好的形式。

接口:

接口中声明的方法,不需要提供关键字public,默认自动属于public,且接口不能含有实例域,也不能在接口中实现方法。提供实例域和方法实现的任务应该由实现接口的那个类完成。接口的关键字implements.接口中的域也不用写public,会被自动设定为public static final.

抽象类与接口:类只能继承一个抽象类,却可以扩展多个接口。

C++中有多重继承,但一般只对“混合”风格的继承使用多继承。混合风格是指一个主要的类描述父对象,其他的基类扮演辅助的角色。这种风格类似于Java的从一个基类派生,然后实现若干个辅助接口。

内部类:

java中内部类可以访问包围它的外部类的私有数据,因为默认编译器给内部类的构造函数加了一个外部类的引用的参数,使其可以访问。

但C++中的嵌套类是不可以访问外部类的任何内容的, java的static内部类与C++嵌套类相似,没有外部类的指针。

局部类:定义在一个方法中的局部类,不能用public或private进行声明,作用域限定在这个声明局部类的块中。局部类对外部世界完全隐藏,即包围这个方法的类也不可见。但局部类还可以访问外部类的内容,且可以访问方法的局部变量,但变量必须被声明为final,之所以可以访问是因为编译器自动在构造函数参数中添加了一个需要调用的局部变量的拷贝,从而即使方法运行结束,仍然可以在局部类中访问方法的局部变量,因此保存成final,不可变,是为了保持局部类中访问的与方法中的变量的一致性。

final变量不可变,但是不是必须在声明时初始化,但是只能被赋值一次,这是与C++ const不同的地方,const必须在声明时被初始化。

匿名内部类语法: new SuperType(construction parameters),例如:

class TalkingClock
{
    public void start(int interval, final boolean beep)
    {
        ActionListener listener = new ActionListener()
        {
            public void actionPerformed(ActionEvent event)
            {
                Date now = new Date();
                System.out.println("At the tone, the time is " + now);
                if (beep)
                    Toolkit.getDefaultToolkit().beep();
            }
        };
        Timer t = new Timer(interval, listener);
        t.start();
    }
}

静态内部类:只是为了把一个类隐藏在另一个类的内部,并不需要引用外围类对象,可以将内部类声明为static, 取消对外部类的引用。声明在接口中的内部类自动成为static和public.

java异常:java中的throws说明符与C++的throw说明符基本类似,但是在C++中throw是在运行时执行,而不是在编译时执行。即C++编译器不处理任何异常说明符。但是如果函数抛出的异常没有出现在throw列表中,则会调用unexpected函数,函数的默认处理方式是终止程序的执行。令外,C++中如果没有给出throw说明,说明函数可以抛出任何异常,在java中则说明不能抛出任何已检查异常。

java泛型:

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型,原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定的变量用Object代替)。原始类型用第一个限定的类型变量来替代。结果泛型类变成一个普通的类。这一点C++与Java有很大区别。C++中每个模板的实例化产生不同的类型,这一现象称为“模板代码膨胀”。java中不存在。例如:

//Pair<T>无限定类型
public class Pair<T>
{
    public Pair()
    {
        first = null;
        second = null;
    }

    public Pair(T first, T second)
    {
        this.first = first;
        this.second = second;
    }

    public T getFirst()
    {
        return first;
    }

    public T getSecond()
    {
        return second;
    }

    public void setFirst(T newValue)
    {
        first = newValue;
    }

    public void setSecond(T newValue)
    {
        second = newValue;
    }

    private T first;
    private T second;
}
//Pair<T>原始类型如下,因为T是一个无限定的变量,所以直接用Object代替
public class Pair
{
    public Pair(Object first, Object second)
    {

    }

    public Object getFirst()
    {
        return first;
    }

    public Object getSecond()
    {
        return second;
    }

    public void setFirst(Object newValue)
    {
        first = newValue;
    }

    public void setSecond(Object newValue)
    {
        second = newValue;
    }

    private Object first;
    private Object second;
}

//假定声明了有限定的类,如下:
public class Interval<T extends Comparable & Serializable>
    implements Serializable
    {
        public Interval(T first, T second);
        {
            if (first.compareTo(second) <= 0)
            {
                lower = first;
                upper = second;
            }
            else
            {
                lower = second;
                upper = first;
            }
        }
        ...

        private T lower;
        private T upper;
    }
//原始类型Interval如下:
public class Interval implements Serializable
{
    //T被替换为第一个限制类型Comparable
    public Interval(Comparable first, Comparable second)
    {
        ...
    }

    private Comparable lower;
    private Comparable upper;
}

java泛型中要注意:虚拟机中没有泛型,只有普通的类和方法;所有的类型参数都用它们的限定类型替换;桥方法被合成来保持多态;为了保持类型安全性,必要时插入强制类型转换。

在虚拟机中,用参数类型和返回类型确定一个方法,因此编译器可能产生两个返回类型不同的方法字节码,虚拟机能够正确处理。

java泛型限制与局限:

1)不能用基本类型实例化类型参数。如int,double,因为泛型的类型擦除,擦除为Object,Object不能存储double ,但可以是Double包装类型。

2)运行时类型查询只适用于原始类型。因为虚拟机中的对象总有一个特定的非泛型类型。因此所有的类型查询只返回原始类型。如

if (a instanceof Pair<String>)  //检测为a instanceof Pair
if (a instanceof Pair<T>)   //T被忽略

//getClass方法总是返回原始类型
Pair<String> stringPair =...;
Pair<Employee> employeePair = ...;
//下面这句是相等的,因为都返回Pair.class
if (stringPair.getClass() == employeePair.getClass())

3)不能抛出也不能捕获泛型类实例,不能扩展Throwable;不能在catch子句中使用类型变量,但在异常声明中可以使用类型变量。即方法后throws T;

4)不能声明参数化类型数组,如Pair<String>[] table = new Pair<String>[10] //是错误的。

5)不能实例化类型变量,不能使用像new T(...), new T[...]或T.class这样的表达式。

6)泛型类的静态上下文中类型变量无效,即不能在静态域或方法中引用类型变量,如:

public class Singleton<T>
{
    public static T getSingleInstance()  //Error,静态方法
    {
        if (singleInstance == null)
            return singleInstace;
    }
    private static T singleInstance; //错误,静态域中
}

7)注意擦除后的冲突

java泛型不能将子类的数组赋值给父类的数组,如:

Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
//非法,不能将子类泛型数组赋值给父类泛型数组
Pair<Employee> employeeBuddies = managerBuddies;
employeeBuddies.setFirst(lowlyEmployee);
但是单纯的数组之间赋值可以:

//单数单纯的数组,可以将子类数组赋值给父类数组
Manager[] managerBuddies = {ceo, cfo};
Employee[] employeeBuddies = managerBuddies; //OK

注意:C++数组不可以这样直接赋值。因为C++不能数组直接赋值。

泛型类可以扩展或实现其他泛型类,这点与普通类一样,如ArrayList<T>类实现List<T>接口,这样一个ArrayList<Mamager>可以被转换为List<Manager>,但一个ArrayList<Manager>不是一个ArrayList<Employee>或List<Employee>.

通配符类型:

Pair<? extends Employee>:表示任何泛型Pair类型,但类型参数是Employee的子类,如Pair<Manager>,但不是Pair<String>.这个限定可以使用get,但不能使用set,即不能向这种类型的数组插入或给变量赋值Emploee类型或其子类(数组可以直接赋值),因为虽然编译器知道这是需要Employee的子类型,但不知道具体是什么类型,它拒绝传递任何特定的类型。但使用get就没有什么问题,因为可以将Employee或其子类赋值给Employee类型,即get可以返回这种类型给Employee,例如:

Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
//这里数组可以赋值
Pair<? extends Employee> wildcardBuddies = managerBuddies;  
wildcardBuddies.setFirst(lowlyEmployee);  //编译错误,类型错误

//Pair<? extends Employee>的get和set可能定义为以下:
? extends Employee getFirst();
vodi setFirst(? extends Employee);
//这样不能调用setFirst,因为编译器不知道具体是哪个类型,只知道是子类型
通配符的超类型限定,即可以指定一个超类型,格式如下:

? super Manager: 这个通配符限定为Manager的所有超类型。这种与之前的extends通配符正好相反,可以提供方法的参数,但是不能使用返回值。例如Pair<? super Manager>有方法:

void setFirst(? super Manager);

? super Manager getFirst();

编译器不知道setFirst方法的确切类型,但是可以用任意Manager对象(或子类型如Executive)调用它,而不能用Employee对象调用。然而如果调用getFirst就不会得到保证,只能把它赋给一个Object.

下面是一个使用? super限定符的典型示例。有一个经理的数组,并且想把奖金最高和最低的经理放到一个Pair对象中。这里Pair可以是Pair<Employee>类型,也可以是Pair<Object>类型,方法可以接受任何适当的Pair:


直观的讲,带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。

无限定通配符,如Pair<?>,方法有:

? getFirst()

void setFirst(?)

getFirst的返回值只能赋给一个Object.setFirst方法不能被调用,甚至不能用Object调用。Pair<?>与Pair本质的区别:可以用任意Object对象调用原始的Pair类的setFirst方法。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值