Java 学习(1)----- java 学习的总体感觉

  好久没有更新博客了,是因为最近在集中精力学习java, Java 的基础知识确实是比 js 多太多了。 学习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  中,就没人给我们创建对象了,只有我们创建对象了。所以,面向对象的编程,就是创建对象,调用对象的方法的过程。

  那这又引出了另外一个问题,怎么创建对象呢?在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 方法中使用。

 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 方法中运行。

转载于:https://www.cnblogs.com/SamWeb/p/10513864.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值