Java 学习(1)----- java 学习的整体感受

  很久没有更新博客了,是由于最近在集中精力学习java, Java 的基础知识确实是比 js 多太多了。 学习java 断断续续的差很少有一年左右的时间, 这一年来,感受懂了一点,过一段时间又忘记了,老是不得要领。如今感受能够了,可以向深一点的方向学习了。java

  学习java 仍是很是痛苦的,尤为是刚开始的时候,由于js 和java 是两种不一样的思想,我习惯了js的函数式编程,而java  则是面向对象的语言,思想的转换是最困难的。还有一个重大的不一样是java 是编译型语言, 代码写好了,要先编译,而后运行。而js 是解释性语言,写好了,直接运行就能够了,我确实习惯了直接运行,这也对个人学习形成了不少误解。学习方法也是比较大众化,对于一无所知的小白来讲,入门教学视频是最好的开始,我从网上找了 毕向东的三十五天的java 学习,如今尚未学完,但懂了一点了,从我学习js 的经验看,最终仍是要落实到书本上,由于只有书籍是系统化的,必定要系统化的学习。百度了一下,推荐最多的java core  和java 编程思想,我先看到java core, 可是看了就忘记了,来回了两三遍,仍是不行,有点失望。java 编程思想也是同样,我想算了,要找别的书籍看了,这和我学习js 是同样的,你们入门的时候,通常都会推荐,JavaScript DOM 编程艺术。可是我看了,并无什么感受,不会写,仍是不会, 直到我看到了 beginning JavaScript 这本书,才算入门了,我给同事推荐的js 的入门书籍都是beginning JavaScript  这本书。java 呢,我找到的两本书或三本入门书籍是,编程

 

  我看到是英文版,应该没有中文版,对java 的总体有了一个完整的认识, 但没有所有看完,由于书的后半部分都是API 的使用,也不是很详细,就没有看了,但整体浏览了一遍,收获很是大,尤为是右侧的书籍,(我怀疑这两本书差很少),我看来Advanced Features , 固然从网上下载的pdf,格式也不是很正确,看起来也有点费劲。总体思想了解了,那就再从细节入手,此次是看 on java 8.  on java 8 是java 编程思想的一书的重写,能够称之为第五版,我没有买,从网上找的pdf, 确实是没有尊重知识产权,没有尊重原书做者, 可是两三百块钱,也买不起。pdf 的格式有点差,我就尝试着抄书,把原书抄一遍,顺便把书上的代码都敲一遍,抄书的过程当中,忽然发现,原来不明白的,有时候抄写一遍就明白了,固然,抄书还在进行中,不过有些知识点也确实理解起来很困难,那就先放一放,用到的时候,才看一看。编程语言

  Java 是面向对象的编程语言,这也就意味着全部的操做都是以对象进行展开,那么对象又怎么理解呢?什么是对象呢?为何要以对象进行展开操做呢?那回到咱们的现实世界来看什么是对象? 当别人问你,”你有对象吗?”, 在这里,对象指的是一我的。为何能够把人称之为对象呢?由于,他有着鲜活的特征,最简单的就是姓名和年龄,其次他还有着不同凡响的功能,会唱歌,跳舞,写代码等。抽象一下,只要有着特性和功能的东西咱们均可以称之为对象,洗衣机了,电冰箱了。明白了什么是对象,那为何要以对象展开操做呢?就是为何要找对象来操做?这就更简单了? 你为何找男友或女友呢?由于,他们确定知足了某一方面的需求,找他们就可以解决。那回到java的世界里,对象也应该有着特性和功能,而且咱们找到它,就能够帮咱们解决问题,用稍微专业一点的术语,就是,每个对象都有着各自的属性和方法,咱们找到这个对象,就能够调用它特有的功能,来实现某个需求。那还有一个问题, 怎么找到这个对象呢,从哪里找到这个对象呢?在咱们现实生活中,电冰箱或洗衣机等,都是别人给咱们建立好的,咱们直接使用,这是建立对象的过程,但在java  中,就没人给咱们建立对象了,只有咱们建立对象了。因此,面向对象的编程,就是建立对象,调用对象的方法的过程。ide

  那这又引出了另一个问题,怎么建立对象呢?在js 中,咱们能够写一个对象字面量,直接定义对象全部的属性和方法,但在java 中不行,建立对象以前,要先描述一下对象,它有哪些属性和方法,怎么描述对象呢?java 定义了一种数据类型:类,那也就意味着建立对象以前要先建立一个类。类是用来描述对象的,它也仅仅是用来描述对象有哪些属性和方法,就像咱们工做中的图纸。房屋建造图纸,它是一张纸,仅仅来标明房屋是由哪些部分组成(属性)和各个部分是怎么链接的(称之为方法吧)。有了图纸(类)以后,咱们并不能作什么,必须建立对象,就是把房子建起来,才能作点什么,好比装修,居住。那咱们写Java代码的顺序,就变成了书写类(描述对象),建立对象,而后调用对象的方法.函数式编程

  那就先建立一个类,这时又会碰到几个问题。首先,这个类写到什么地方?放到什么文件中?再广一点,就是咱们写的java 代码放到什么文件中?全部的java 代码都放到.java文件中. 固然,文件名也是有讲究的,这里先暂且不说,直接命名一个Person 好了,那咱们就要建立一个Person.java 的文件来存放类代码。先建一个java 文件夹,而后再在该文件夹下建Person.java 文件。如今终于能够写java 代码了,那用什么代码编辑软件写呢?有idea, eclipis 集成开发工具,也有Notepad++, vscode 等编辑软件, 简单起见,我这里面就使用vscode了(最好安装一个Java插件)。用vscode 打开Person.java 文件,开始写类,那怎么写类? 类的语法又是怎么样的?首先以class 关键字开始,表示它是一个类,其次是类名,类名就是一个变量,不过要以大写字母开始, 如:Person,而后就是一大括号{}, 大括号里面就是属性和方法。Person 类有哪些属性?姓名和年龄,有哪些方法?吃饭,睡觉,说话等。那如今就要把这些属性和方法定义到person 中,怎么定义呢?属性的定义和变量的定义同样,方法的定义和函数的定义同样,不过java 是强类型,声明变量和函数,都要求注明类型。姓名是字符串类型,用String, 年龄是数字类型,用int . String name; int age; 就是定义了属性。方法(函数)定义:返回的类型 函数名(接受的参数列表){ 函数体}, 若是没有没有返回类型,则使用void, 定义一下吃饭 void eat() {}, 那一个完整的Person 类,以下函数

class Person {
  String name;
  int age;

  void eat() {
    System.out.println("吃饭"); // 简单打印一下
  }

  void sleep(int time) {
    System.out.println("睡觉" + time + "个小时");
  }

  void speak() {
    System.out.println("姓名"+name+ " 年龄 " + age ); // 方法调用属性。
  }
}

  类定义好了(固然这是最简单的),那接下来就是建立对象,调用对象方法,完成需求了。这其实涉用到了代码执行的问题,就是当程序执行的时候,执行哪一段代码。在java 中,每个类均可以定义一个main方法,java 程序执行的时候,就会执行main 方法,它的格式也是固定的。public static void main(String[] args) {方法体}, 方法体就是要执行的代码,咱们就能够在里面建立对象,调用方法。建立对象用的是new 类名(new Person()), 建立成功后,要把它赋值一个变量,这样咱们好使用这个对象,调用方法。这时怎么声明一个变量来接受建立的对象呢?上面说了,声明变量的语法是类型 变量名。 变量名很简单,命名为person1 好了。那这个变量的类型呢, 它属于什么类型? 由于咱们是使用Person 类来建立的对象,那么这个对象就属于Person,  声明的变量也应该是Person 类型。Person person1 就声明变量成功了。完整的代码以下工具

class Person {
  String name;
  int age;

  void eat() {
    System.out.println("吃饭"); // 简单打印一下
  }

  void sleep(int time) {
    System.out.println("睡觉" + time + "个小时");
  }

  void speak() {
    System.out.println("姓名" +name + " 年龄 " + age ); // 方法调用属性。
  }

  public static void main(String[] args) {
    Person person1 = new Person();
    person1.eat();
  }
}

  代码写完了,就要开始运行了。但对于java来讲,它是一门编译型语言,要先编译,再运行。这就要求咱们先安装java的运行环境和编译命令等等,就是安装jdk, 配置环境变量( 配置稍后再说),配置完成后, 就可使用javac 命令来编译程序,java 命令来运行程序。找到Person.Java 文件所在的文件夹,而后在该文件夹下,打开cmd 命令窗口,执行javac  Person.java 命令,报错了。因为个人vs code 全部的文件默认是utf-8 编码,因此编译的时候要提供文件编码, javac -encoding UTF-8 Person.java, 这样就OK了。编译成功后, 你会看到文件夹里多了一个Person.class 文件,这就编译好的文件,咱们再调用java 命令执行这个文件,java Person, 你会发现输出了吃饭。这就是整个java 程序的执行过程。学习

  对于大型的java  程序来讲,它不可能只有一个类,而是由无数个类组成的,这时类的命名就会是个问题,每写一个类的时候,还要想想,之前是否是有过这个类名,总不可能冲突了吧?但谁会记得之前的类都是什么名字呢?为了避免形成命名冲突,你可能会把类名写得很长,很个性,其实java 提供了一个更好解决类名冲突的方法,那就是包。咱们能够把类放到不一样的包下面,这样,这个类就属于这个包了,减小了命名冲突。包,其实就是文件夹,咱们能够把不一样的类文件放到不一样的文件夹下。如今Person 类放到了java文件夹下,当咱们再写一个类的时候,咱们不在java文件夹下建类文件了,能够新建一个文件夹,如world, 而后,再在world 里面建一个类文件,如Person.java。这时候Person 就是属于world 了,和外面的Person 就没有冲突了。咱们在java代码中,是怎么告诉Person 类是属于world的。那就用package 关键字。在world 文件夹下Person.java 中,写一个package world, 而后把speak 改为以下开发工具

package world;

class Person {
    void speak() {
        System.out.println("在world 文件夹下" ); // 方法调用属性。
    }
}

  这里要注意,package和 文件夹的名称是一一对应的,若是咱们写package world.hello; 那么它对应的文件夹应是world 文件夹下面再有一个文件夹hello, 而后在hello 文件夹下有一个Person 类。package 语法中的点号,就表明下一级文件夹。当一个类有所属的时候, 就不能直接使用类名了,而是要把它的所属也带上,Person 类的真正名称是 world.person. 建立对象时 world.Person person2 = new world.Person()。这时,能够在外面的Person.java 中main 方法中使用。this

 public static void main(String[] args) {
        Person person1 = new Person();
        world.Person person2 = new world.Person(); // 使用world 包下在的Person 类
     person2.speak();
person1.eat(); }

  这时 javac -encoding UTF-8 Person.java,又报错了。

  Person 类在world 中不是公有的,这又涉及到java 的权限问题,当一个类有了包所属后,它能够被它所在的包中的全部java类引用,但不能被外面的包引用。若是一个类要让外界访问,那必须加一个修饰符public , 在class 关键字前面加一个public, 这时类也分为了两种,一种是是public修饰的,一个是默认的(没有public 修饰),当这个类前面有public时,这个类能够被其它包中的类进行引用。如:public class Person {}. 可是若是这个类前面什么都没有,它只能在本包中进行使用,不能被其它包所引用。修改一下world 文件夹下的Person类。

package world;

public class Person {
    void speak() {
        System.out.println("在world 文件夹下" ); // 方法调用属性。
    }
}

  当一个类前面有public 的时候,这个类所在的文件的名称,必须和类名一致,这也就是文件名为Person.class的缘由。若是你修改文件名为其它(如Student.java),则会报错。同时一个文件中只能有一个public class,和文件名保持一致。好了,如今再编译一下, 你会发现又一个错误

  speak() 方法,不是公共的, 这又是权限问题,不过是类的内部的权限。类中的属性和方法也有访问权 限问题,它的更为复杂一点,由于方法前面能够加的修饰符比较多,private, public, protected修饰符

  类中的成员或方法:

  若是前面是public, 这个方法或属性,能够在任何地方被使用。

  若是前面是private, 这个属性或方法,只能在本类中使用。

  若是前面是默认,什么都没有,则能够在包中被引用。

  若是前面是protected, 它能够在本包中的其它类中进行使用,而且能够被其它包中子类使用。只有子类才能使用。

  那在这里,咱们要使用public修饰符,改一下。

package world;

public class Person {
    public void speak() {
        System.out.println("在world 文件夹下" ); // 方法调用属性。
    }
}

  再改一下

  当咱们使用一个类的时候,咱们应该先找到这个类,这个类前面的属性要么是public, 要么什么都没有,若是没有,其它包中的类就不能使用这个类。肯定使用这个类之后,就要肯定可否使用这个类的属性或方法,它前面又有四个修饰符。

  这时,你会发现当类有所属的包以后,再使用这个类就不是很方便了,要把包名和类名 连在一块儿使用。这时java 提供了导包(import)机制, 咱们先把包导入到要使用包的java文件中,而后,使用包中的类时,就和咱们日常的使用方式同样了 直接使用类名。 可是若是导入的包中和使用包的java 文件中,有重名的类名时,像咱们这里,world 包中有Person, Person 类中 也有Person, 就不能直接使用类名了,由于若是使用类名,java 并不知道 要使用哪一个包中的Person类,仍是要和如今的使用方式一致,包和类名要写全了。咱们在world文件夹中再声明一个类,如Student, 体验一下导包。

package world;

public class Student {
    public void speak() {
        System.out.println("在world 文件夹下Student" );
    }
}

  而后在java 文件夹中Person 类,import world.*; 导入world 包, 而后在main 方法中,直接使用Student。

import world.*;  // 导入world 包中的全部方法
class Person {
    String name;
    int age;

    public static void main(String[] args) {
        Student s = new Student();
        s.speak();
    }
}

  其实当咱们写了大量的类之后,当你再写一个类的时候,你会发现,这个类怎么和之前的一个类这么类似呢,或者,某个类能够实现这个类的部分功能?这就是java 中的继承 和组合。好比,咱们写一个汽车类,忽然发现之前有一个轮胎类,这时咱们就能够直接把轮胎类对象放到汽车类做为属性直接使用了,这就是组合,组合和现实中的组合没有区别,组合一个类,和搭积木同样,它体现了一种has- a 的关系。继承,则是体现一种is-a 的关系,它是它的关系。当咱们建立一个工人worker 类的时候,你发现了Person 类 ,工人也是人啊,绝对体现is-a 的关系,这时就能够继承了,使用extends 关键字。class Worker extends Person {} , 这时 Worker 类就彻底拥有了Person 类的属性和方法(private 属性除外)。当使用Worker 建立一个对象的时候,它能够调用person 对象上的方法。把根目录下面的Person 类改成以下内容(增长Worker)

class Person {
    private String name;
    private int age;

    void eat() {
        System.out.println("吃饭"); // 简单打印一下
    }

    void sleep(int time) {
        System.out.println("睡觉" + time + "个小时");
    }
}

class Worker extends Person {

    public static void main(String[] args) {
        Worker w = new Worker();
        w.eat();
    }
}

  编译运行一下  

  没有问题。那Worker类能不能增长新的方法和属性呢?固然能够,Worker自己也是一个单独的类,你想加什么方法就加什么方法, 那能不能增长和父类同名的方法好比eat, 也能够,这叫覆写,由于子类彻底能够拥有本身的eat 方法。

class Worker extends Person {

    void eat() {
        System.out.println("吃馒头"); // 
    }

    void work() {
        System.out.println("工做"); // 工人特有的方法
    }
    public static void main(String[] args) {
        Worker w = new Worker();
        w.eat();
    }
}

  这时,你会发现,每个子类都至少拥有父类的方法,子类只会比父类越来具体,父类就至关于愈来愈抽象,若是父类不停的抽象,你会出现,它只剩下描述了,方法根本没有具体的实现方式,太抽象了,也没有办法实现,只能有子类进行实现,这就是接口interface。interface 里面的方法只用描述,没有实现。

interface Man {
    void eat();
}

  interface 的语法和class 类似,只不过方法没有实现。interface 确定不能直接用,要有一个类实现它,而后建立实现类的对象, 实现一个接口用的是implements 关键字

class Student implements Man {
    public void eat() {
        System.out.println("喝牛奶"); // 简单打印一下
    }
    
}

  这时候出现了一个神奇的事情,咱们能够用interface 声明一个引用变量 Man m, 而后指向实体类对象 m = new Student(), 这就造成了多态。 由于一个接口能够有无数个实现类,m 就能够指向无数个实现类对象, 也就拥有了多个形态。赋值成功之后,student 对象(类型)就转化成了Man 类型,发生了向上转型。转化成Man 类型后,它就不记得它指向的具体类型,因此只能获取到Man类型 定义的属性和方法,在这里就是eat() 方法。m.eat().

class Student implements Man {
    public void eat() {
        System.out.println("喝牛奶"); // 简单打印一下
    }
    public static void main(String[] args) {
        Man m = new Student();
        m.eat();
    }
}

  当调用eat 方法以后,你发现调用的居然是Student中的方法,不是已经发生向上转型了,怎么仍是调用实现类(子类)的方法, 这里要注意的是eat 方法是对接口(父类)的覆写。这又涉及到方法的动态绑定, 方法只有真正运行的时候,才能肯定调用哪一个方法,这是java 虚拟机自动选择的。

  It is important to understand that it is the type of the reference variable(the Man) --- not the type of the object(Student) -------that determines what members can be accessed. That is, when a reference to a subclass object( new Student()) is assigned to a superclass reference variable(Man), you will have access only to those parts of the object defined by the superclass. If you think about it, this makes sense, because the superclass has no knowledge of what a subclass adds to it. 声明的引用变量m 决定了你能够调用哪些属性和方法

  Method overriding forms the basis for one of java’s most powerful concepts: dynamic method dispatch. Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time rather than compile time. Dynamic method dispatch is important because this is how java implements run-time polymorphism. 方法覆写组成了java 中的动态方法绑定。只有在运行的时候才能肯定执行哪一个覆写的方法

  Let begin by restating an important principle: a superclass reference variable can refer to a subclass object. Java uses this fact to resolve calls to overridden methods at run time. When an overridden method is called through a superclass reference, java determines which version of that method to execute based on the type of the object being referred to at the time the call occurs. Thus this determination is made at run time. When different types of objects are referred to ,different versions of an overridden method will be called. In other words, it is the type of the object being referred to (not the type of the reference variable) that determines which version of an overridden method will be executed.

  Polymorphism is essential to object-oriented programming for one reason: it allows a general class to specify methods that will be common to all of its derivatives, while allowing subclasses to define the specific implementation of some or all of those methods. 

  若是真的要用m 变量引用具体的类型(Student中)的方法呢?只能强制类型转化,把m 从Man 类型转化成Student 类型,Student s = (Student) m.  m.speak(). 假设有speak 方法。

  如今就能够真正的写java 程序了,可是你也不用每个类都本身写,若是第三方提供了,拿过来直接用就能够了,何须造轮子呢?因此不光要学java的语法,还要第三方的API.

  总结一下:

  Java 程序是类组成的,类又是由属性和方法构成。在程序开发的过程当中,咱们能够书写每个类,可是绝大多数的程序开发者都会使用第三方提供的类库。所以在学习java 的时候,不光要学习怎么书写java 代码来建立本身的类,还要学习其它类库提供了API.

   Java 程序呢?每个java 文件都要以.java 结尾,文件名必须与文件中的public class的类名相同,若是java 文件中没有public class 呢? 这时你能够命名任何名称,不过,仍是建议与文件中的类保持一致。一个java 文件只能有一个public class, 可是能够任意多个no-public class. 若是一个文件中有多个class, 编译的时候,全部的类都会编译到当前文件名,文件名就是类名,后缀则以.class 文件结尾。具体到每个文件都有什么内容呢?

  文档注释:写过代码的人都很熟悉,就是代码是何时建立的,建立人是谁。

  package 语句:一个java 文件中最多只能有一条包语句,表示文件的命名空间。若是有包语句的话,它必须放到文件的最前面,也就是文件的第一句。固然文档注释不算在内,文档注释能够放到package 语句的前面。

  import 语句: 一个文件中,能够有多条import 语句,引入文件开发所须要的包。它要放到package 语句的后面,任何自定义类或接口的前面。

  接口(interface)或类(class)类定义: 一个文件中,能够有多个接口,也能够没有接口。能够有多个class, 但至少得存在一个class, 要否则程序没有办法运行。 之因此存在 一个类,是要有一个main 方法,java 程序运行的时候,就是找main 方法。一个最简单的java 程序可能就是这一个类,全部的代码都放到main 方法中运行。

相关文章
相关标签/搜索