java中容易犯错的知识点

java中容易犯错的知识点

该博客的内容是在看了《head first java》之后总结的一些知识点,是针对我个人的不足总结的知识点,我写博客还是newer,希望大神们多多指教。需要注意的是这篇内容的标号和《head first java》中的章节标号不是一致的。

目录


No.1 对象数组

我曾经在一家公司实习的时候,学习的是C#。虽然都是C类语言,但其实它和JAVA的语法之类的更为匹配一些,当时初学的时候就范了一个错误:

class Dog{
    String  name;
    Dog(String na){
        name = na;
    }
    public void setName(String na){
        name = na;
    }
    public String getName(){
        return name;
    }
}

public class DogTest{
    public static void main(String[] args){
        Dog[] dog = new Dog[7];
    }
}

上面的这段代码你知道哪儿错了吗?解答如下:
《head first java》中的解释
《head first java》中的解释

就像买东西的时候一般都是从口袋里面拿出钱包,再从钱包里面拿出钱。这个钱包就相当于一个引用,但是如果你钱包里面没钱,即使你有钱包也买不到东西(当然,要是老板同意让你拿钱包抵押,那是另外一回事了。。。)。所以 Dog[] dog = new Dog[7];这句代码只相当于你得到了钱包,但是不幸的是钱包里面并目前钱(即对象),所以需要放点钱进去 dog[0] = new Dog(“man”);

No.2 java中的修饰符的访问权限

C++和java都是面向对象的,但是呢,我个人觉得真正的面向对象是JAVA。虽然大学四年都在和C++打交道,但是连《C++primer》都没看过的我来说,很多知识点是懵的。BUT,我还是先来整理一下关于JAVA的好啦。
需要注意的是:在java中类的成员变量默认为default ,但是在C++中默认为private。
修饰符权限表格如下:

访问权限同一个包子类其它包
public
protected×
default××
private×××

同时还有类的其它两个修饰符abstract (修饰的类只声明方法,并不实现)、final(修饰的类不能被继承)

No.3 局部变量与实例变量

局部变量是某个方法的参数或者内部需要时候才声明的变量,这个时候局部声明的变量必须初始化才能使用,但是类中的实例变量不需要在声明的时候初始化。Eg:

class Test{
    String name;
    public void go(){
        String test;
        name = name + "right"; //可以通过
        test = test + "wrong";//this is wrong!!!必须初始化,因为局部变量没有默认值。
    }
}

No.4 C++与JAVA中创建对象的不同

毕竟我大学四年搬运的代码都是C++的,所以还是需要对比一下两者的不同。JAVA中已经木有了指针的这个概念,其实java中的引用在某些地方是可以替代C++中的指针的作用的。
C++中创建对象

1. Test test;//在栈中
2. Test* test = new Test();  
    delete(test);//分配的内存在堆中,因为C++中木有GC,所以这种创建对象的方式需要手动删除对象
    class Test{
        String name;
}

JAVA中创建对象的方式:

Test test = new Test();//分配的内存是在托管堆中,因此并不需要手动删除对象。test是对托管堆中对象的引用。

No.5 变量的比较

  1. 使用“==”来比较两个基本类型,或者判断两个引用是否指向同一个对象。因为”==”比较是是字节组合
  2. 使用equals()来判断两个对象是否在意义上相等

No.6 & 和&&的区别

  1. 虽然&和&&都是逻辑运算符,但是在运行的时候会有差别。
  2. 两者的差别如下:
System.out.println(false & (1/0 == 0));//这句话会抛出异常,因为除数不能为零
System.out.println(false && (1/0 == 0));//这句话会打印出false

Note:这两句话的不同只在于运算符的不同,所以这说明:&运算符会判断左右两边所有条件,但是&&运算符如果前者能决定结果,那么后面的就可以不用判断了。

No.6 JAVA 中的多态

可以参考java学习之多态,这篇文章写得比《head first java》中清楚得多,毕竟我觉得《head first java》这本书的中文版翻译还是存在很多问题的。总结来说呢,java多态需要注意以下几点:
1. 覆盖属于多态,但是函数重载(不能只改变返回类型)不属于多态。
2. 多态==晚绑定,因为多态是一种运行期的行为,不是编译期的行为
3. 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如:

class Animal{
    String name;
    public void eat(){
        //eat food;
    }
}
class Cat extends Animal{
    public void makeNoise(){
        //make cat noise;
    }
    public void eat(){
        //Cat eat;
    }
}

class Dog extends Animal{
    public void eat(){
        //Dog eat;
    }
}

//main
class Test{
    public static void main(String[] args){
       //向上类型转换
        Cat cat = new Cat();
        Animal animal = cat;
        animal.eat();//right,eat()函数在animal类中也存在,但是       调用的是Cat的eat()函数。
        animal.makeNoise();//wrong ,makeNoise()不存在父类中
        //向下类型转换
        Animal animal = new Cat();
        animal.makeNoise();//wrong,makeNoise()在animal父类中不存在。
        Cat cat = (Cat)animal;
        cat.eat();
        cat.makeNoise();//right,调用cat中的makeNoise()函数。
    }
}

总结就是:可以用父类代表子类。

No.7 抽象的方法和类

  1. 抽象的类中可以有static 的成员,抽象与非抽象的方法;(static修饰的变量表示一个类有且仅有一个,有种全局的感觉)
  2. 不能在非抽象的类中拥有抽象的方法,也就是要是一个类有抽象的方法,该类必是抽象类;
  3. 抽象的类表示一定要被extends;

No.8 构造器和垃圾收集器

关于构造器,主要有以下几点:
1. 在创建新对象的时候,所以继承下来的构造函数都会执行,也就是父类的构造函数都会执行一遍。
2. 某个类的构造函数若修饰符是private,那么代表这个类不能被实例化。
3. 使用this()来从某个构造函数调用同一类的另外一个构造函数,this()只能用在构造函数中,且必须是第一行语句,super()(对父类构造函数的调用)与this()不能兼得。例如 :

class Cat extends Animal{
    Color color;
    public Cat(){
        this(Color.red);//对该类中的下一个构造函数进行调用
    }
    public Cat(Color c){
        super("Cat");//对父类构造函数的调用
        color = c
    }
}

关于垃圾收集器,java虚拟机的GC机制可以自动回收托管堆中的对象,至于进行回收的算法,那是另外一件事情啦。

No.9 静态static

  1. 类中的静态变量对所有的实例都是相同的。
  2. static final double PI = 3.14,final代表不可变的。
  3. 静态的方法不能调用非静态的变量,因为它并不知道这个非静态的变量是属于哪个实例的。
  4. Java中会把常量标记为 final和static的,必须在声明或者静态初始化中赋值。两种如下:
1. 
pubic class FOO{
    public static final int FOO_X = 25;
}
2.
public class FOO{
    public static final int FOO_X;
    static{
        FOO_X = (int)Math.random();
    }
}

No.10 基本数据类型的包装——autoboxing

因为有些时候想把基本数据类型当做对象来处理,就需要对其进行包装,解包。Java5.0之后就加入了autoboxing

//无autoboxing
public void doNum(){
    ArrayList listNum = new ArrayList();
    listNum.add(new Integer(3));//不能直接加入3,得先转为对象
    Integer one = (Integer)listNum.get(0);//返回Object类型,转为Integer类
    int intOne = one.intValue();
}

//有autoboxing
public void doNum(){
    ArrayList<Integer> listNum = new ArrayList<Integer>();
    listNum.add(3);//直接加入3,编译器自动装包
    int one = listNum.get(0);//编译器自动完成解包

No.11 图形用户接口

Question: 当每个按钮执行不同工作时,要如何对多个不同的按钮分别取得事件?
Answer: 内部类!!内部类可以外部类的所有方法和变量。内部类的实例一定会绑定在外部类的实例上。
内部类与外部类的关系如下:
内部类与外部类的关系

监听GUI事件:

多个按钮图形处理

Notice:非静态内部类的实例必须通过外部类的实例才能获得,但是对于静态的内部类不需要外部类的实例就可以调用,但是静态内部类只能调用外部类的静态变量。

Question:当你在编写GUI程序的时候,突然发现你需要实现一个ActionListener的类的实例,但是你发现你没有任何该类的实例,之前也没有写个该类,那你该怎么做呢?
Answer:一是在程序中实现内部类,二是当场创建出匿名的内部类。如下:

public class test{
    public static void main(String[] args){
        JFrame frame = new JFrame();
        JButton button = new JButton("click");
        frame.getContentPane().add(button);
//  button.addActionListener(quitListener);//通常是传入一个内部类的实例
        buttton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                System.exit(0);
        }});//现场定义了一个类,并且创建了它的实例。
    }
}

No.12 保存对象(序列化)

  1. 要存储对象的状态,有两种办法:一. 写入纯文本文件;二. 将其进行序列化。
  2. 要让类能够被序列化,必须实现Serializable
  3. static不会被序列化,如果某个变量不想被序列化,就标记为transient。
  4. 进行序列化代码如下:
//序列化到文件
FileOutputStream fileStream = new FileOutputStream("my.ser");
ObjectOutputStream os = new ObjectOutputStream(fileStream);
os.writeObject(one);
os.close();

//解序列化
FileInputStream fileStream = new FileInputStream("my.ser");
ObjectInputStream os = new ObjectInputStream(fileStream);
Object one = os.readObject();
Game two = (Game)one;
os.close();

No.13 网络与线程

一. 网络联机

java中也是通过Socket进行网络的连接,连接图如下:
socket连接过程

public class DailyAdvice{
    public void go() {
        try {
            Socket sock = new Socket("127.0.0.1", 5555);//建立连接
            InputStreamReader streamReader = new InputStreamReader(sock.getInputStream());//用StreamReader读取服务器传过来的内容。
            BufferedReader reader = new BufferedReader(streamReader);

            String advice = reader.readLine();

            reader.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    public static void main(String [] args) {
        DailyAdvice client = new DailyAdvice();
        client.go();
    }
}

二. 线程的建立过程

public class MyRunnable implements Runnable{
    public void run() {//必须要实现,这是thread运行所需执行的任务
        go();
    }

    public void go() {
        doMore();
    }

    public void doMore() {
        System.out.println("something");
    }
}

class ThreadTester{
    public static void main(String [] args) {
        MyRunnable job = new MyRunnable();
        Thread th = new Thread(job);
        th.start();//开启线程
    }
}

一旦线程进入可执行状态,就会在可执行和执行中两种状态切换。

三. 多线程,并发性、死锁预防

  1. 线程的并发性会导致致命的错误,所以必须对一部分代码进行同步化。即用synchronized这个关键字来修饰方法使其每次只能被一个线程调用。事实上,每个对象,每个类都会有一个锁,同步化就是把锁变成有用的工具。当然也可以只同步化需要同步化的几行代码:
public void go(){
    synchronized(this){//需要同步化的代码
        //critical stuff
    }
}
  1. 当然多线程也会发生死锁,所以要避免死锁。
  2. 多线程的实例
//服务端程序
public class ChatServer {
    ArrayList clientOutputStream; 
    public class ClientHander implements Runnable{
        BufferedReader reader;
        Socket sock;

        public ClientHander(Socket clientSocket) {
            try {
                sock = clientSocket;
                InputStreamReader isReader = new InputStreamReader(sock.getInputStream());
                reader = new BufferedReader(isReader);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }

        public void run() {//thread会运行的程序
            String message;
            try {
                while((message = reader.readLine()) != null) {
                    System.out.println("read "+ message);
                    tellEveryone(message);
                }
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }
    public void go() {
        clientOutputStream = new ArrayList();
        try {
            ServerSocket server = new ServerSocket(5006);

            while(true) {
                Socket clientSock = server.accept();
                PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
                clientOutputStream.add(writer);

                Thread t= new Thread(new ClientHander(clientSock));
                t.start();//会产生新的线程
                System.out.println("got a connection");
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }//end go
    public void tellEveryone(String message) {
        Iterator it = clientOutputStream.iterator();
        while(it.hasNext()) {
            try {
                PrintWriter writer = (PrintWriter)it.next();
                writer.println(message);
                writer.flush();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }

    //main
        public static void main(String[] args) {
            new ChatServer().go();
        }
}

No.14 集合与泛型

1 . 集合中的关系如下图:
集合API
2. 当使用compareTo()函数的时候,list中只可以有一个将自己与同类型比较的方法。所以可以使用comparator这个独立的类,自制比较函数。如下:
comparator类的实现

  1. 泛型
    下面两个方法的声明
    public void eat(ArrayList list){}
    public void eat(ArrayList list){}
    前者可以使用任何的animal,如可以使用ArrayList作为参数传递,但是后者只有ArrayList才是合法的。与第一句相同功能的声明如下:
    public void eat(ArrayList

No.15 远程部署

其实这部分看到后面就没力气了,所以先放一放,等我看完再来更新好啦!(谁可以告诉我:怎么才能充满power~)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值