*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}

.hljs-comment,
.hljs-template_comment,
.diff .hljs-header,
.hljs-javadoc {
color: #998;
font-style: italic;
}

.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.javascript .hljs-title,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #333;
font-weight: bold;
}

.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #099;
}

.hljs-string,
.hljs-tag .hljs-value,
.hljs-phpdoc,
.tex .hljs-formula {
color: #d14;
}

.hljs-title,
.hljs-id,
.coffeescript .hljs-params,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}

.javascript .hljs-title,
.lisp .hljs-title,
.clojure .hljs-title,
.hljs-subst {
font-weight: normal;
}

.hljs-class .hljs-title,
.haskell .hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}

.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rules .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}

.hljs-attribute,
.hljs-variable,
.lisp .hljs-body {
color: #008080;
}

.hljs-regexp {
color: #009926;
}

.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}

.hljs-built_in,
.lisp .hljs-title,
.clojure .hljs-built_in {
color: #0086b3;
}

.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}

.hljs-deletion {
background: #fdd;
}

.hljs-addition {
background: #dfd;
}

.diff .hljs-change {
background: #0086b3;
}

.hljs-chunk {
color: #aaa;
}

#container {
padding: 15px;
}
pre {
border: 1px solid #ccc;
border-radius: 4px;
display: block;
background-color: #f8f8f8;
}
pre code {
white-space: pre-wrap;
}
.hljs,
code {
font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
}
:not(pre) > code {
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
background-color: #f9f2f4;
white-space: nowrap;
border-radius: 4px;
}
-->

基础

类有属性和方法,它们对本类有效(作用范围)。类的属性就是成员变量,它默认会赋值初始化。类的方法是类具有的一些行为。

类是抽象的,将它们实例化后就是对象(通过new进行实例化),各实例化后的对象都具有这些成员变量的属性,且赋有具体的值,如果某对象没有为成员变量赋值,则采用默认初始化时的值。每个new出来的对象都有自己独立的成员变量,但某个类的所有对象都共享类的方法,因为类方法只是一段放在代码区的代码,只有执行调用类方法时才会产生相关内容。

有了实例化后的对象,就可以引用对象的属性并调用对象的方法(实际上是类的方法,方法是共享的,并不属于某个单独的对象),这样就可以实现这个对象的相关操作。引用对象的属性方式为"对象名.成员变量",调用对象的方法的方式为"对象名.方法"。虽说方法是各对象共享的,但显然,"对象名1.方法1"的方法1执行时,方法内部的变量采用的都是对象名1的成员变量。

示例分析:

以一个三维空间上的点类来说,点具有三维坐标xyz,x、y、z就是它们的属性,需要定义为点类的成员变量。点可以求出它到原点的距离、到另一个点的距离,求距离就是通过类的方法(函数)实现。通过new这个点类,就可以实实在在地创建一个点,new一次就一个点,每个点都有自己的xyz属性,每个点的成员变量都在new出来的时候和对象一起存放在heap内存区。每个点都拥有大家共享的求距离方法getDistance()。于是,就可以将这个点类定义为下面的形式:

class Point {

    //定义成员变量,即三个坐标,坐标可能是小数,因此定义为double类型
double x;
double y;
double z; //定义构造方法,使得以后在new一个点对象的时候为点的成员变量xyz赋值
Point(double _x,double _y,double _z) {
x = _x;
y = _y;
z = _z;
}
//有了构造方法,就可以new对象的时候赋值,例如赋值点p对象的xyz分别为1/2/3:
//Point p = new Point(1.0,2.0,3.0); //定义求两点间距离的方法getDistance()。
//涉及到两个点:一个是调用该方法的点对象自身,一个是目标点对象
//因此,需要将目标点对象作为方法的形参,并使用目标点的坐标属性
double getDistance(Point px) {
return (x-px.x)*(x-px.x)+(y-px.y)*(y-px.y)+(z-px.z)*(z-px.z);
} //有了方法,以后就可以调用该方法求距离,例如,求点(1,2,3)到原点(0,0,0)的距离
//Point p = new Point(1.0,2.0,3.0);
//Point p1 = new Point(0.0,0.0,0.0);
//System.out.println(p.getDistance(p1));
//这表示调用点p的方法,求点p到原点p1的距离,
//p1的属性赋值给方法的形参px(px指向点p1对象),因此其坐标值为(0,0,0)
//因为调用的是点p的方法,因此方法中的x/y/z是点p的成员变量值,即(1,2,3)
}

将上述代码整理,并写一个main方法,就可以实现一个计算两点距离的小程序。例如,TestPoint.java文件内是如下内容:

class Point {
double x,y,z; Point(double _x,double _y,double _z) {
x = _x;
y = _y;
z = _z;
} double getDistance(Point p){
return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)+(z-p.z)*(z-p.z);
}
} public class TestPoint {
public static void main(String[] args) {
Point p = new Point(1.0,2.0,3.0);
Point p1 = new Point(1.0,0.0,0.0);
System.out.println(p.getDistance(p1));
}
}

从上面的例子中可以感受到,面向对象更抽象地说是面向类。在实现某个功能的时候,例如求两个三维点之间的距离时,将点的属性和求距离的方法定义到点类中,以后就不用管点的xyz属性、求三维点之间距离的表达式方法,只要在需要时面向这个类new出点对象,它就有了点的xyz属性,再调用点对象的求距离的方法就可以了。有了面向对象,求三维点距离时,只需知道两件事:为成员变量xyz赋值;记得点类中的方法的名称。这就像为了查看文本内容执行cat命令一样,只要记得cat命令的名称、功能和选项参数即可,无需关心cat的内部机制是如何读取文本内容并将其显示出来的。

在定义一个方法时,需要考虑三个问题:方法的名称如何取、方法的参数、方法是否有返回值。方法的名称暂且不说,方法的参数必须要考虑清楚,例如求两点的距离时,参数可以是某个点的坐标,也可以直接是一个点对象。如果已经定义了点类,那么使用点对象作为参数更符合面向对象的原则;方法的返回值同样重要,返回值决定了这个方法的性质,例如判断两点间的距离是否大于20,就应该返回布尔类型,而不是double类型。

构造方法

构造方法和类同名,它的作用是在对象被new出来时做初始化行为。因为构造方法的目的是初始化,因此构造方法必须不能有返回值,即不能写上数据类型或void关键字。

例如:

class Point {
double x,y,z; //构造方法,注意其名称必须为Point()
Point(double _x,double _y,double _z) {
x = _x;
y = _y;
z = _z;
}
}

以后就可以在new对象的时候进行初始化,例如:

Point p = new Point(1,2,3);

如果没有显式定义构造方法,则隐含了一个空构造方法,例如下面的代码中,两个class是完全等价的。

class Point {
double x,y,z;
} class Point {
double x,y,z;
Point() {}
}

因为初始化有默认的值,所以它们还等价于(0.0是初始化double时的默认值):

class Point {
double x,y,z;
Point() {
x = 0.0;
y = 0.0;
z = 0.0;
}
}

正因为有隐含的空构造方法,才能在new对象的时候不使用任何参数就能进行成员变量的初始化。例如:

Point p = new Point();

在new对象的时候,对象的参数必须和构造方法完全对应,例如定义了构造方法Point(double _x,double _y,double _z)后,就只能new Point(value1,value2,value3),而不能不接任何参数new Point()或接少于3个的参数new Point(value1,value2)

方法的重载(overload)

当两个或多个方法的名称相同,只有参数不同时(可以是参数的个数、参数的数据类型不同),它们就构成了方法的重载。

方法的重载大大减少了方法数量的定义。例如,要求两个值中较大者,考虑到值可以是整形也可以是小数,于是使用如下方式:

public class Num {

    void intMax(int a,int b) {
System.out.println(a>b ? a : b);
} void doubleMax(double a,double b) {
System.out.println(a>b ? a : b);
} public static void main(String[] args) {
Num n = new Num();
n.intMax(2,3);
n.doubleMax(2.0,3.0);
}
}

这里第一个方法intMax()和第二个方法doubleMax()实际上是重复的,仅仅只是参数类型上不同。这样的设计很不方便,不仅在比较数值时不知道应该调用哪一个方法,还要知道各个方法的区别。

而使用重载就不再有这样的问题。

public class Num {

    void Max(int a,int b) {
System.out.println(a>b ? a : b);
} void Max(double a,double b) {
System.out.println(a>b ? a : b);
} public static void main(String[] args) {
Num n = new Num();
n.Max(2,3);
n.Max(2.0,3.0);
}
}

这两个Max方法名称相同,仅仅只是参数不同。在调用Max()的时候,根据传递的实参(2,3)和(2.0,3.0),它能能够区分出前者应该使用第一个Max(),后者使用第二个Max()。

重载的本质是在调用方法时能够通过传递的参数个数、参数的值筛选出具体应该使用哪个方法。

例如下面的方法中,前4个都能构成方法重载,而最后一个不能,因为它的定义方式不同。从本质上来说,是因为调用Max()传递两个int整型数值时,无法确定是选择第一个方法还是最后一个方法,而它们又正好是冲突的,因此它们不构成重载。

void Max(int a,int b) {}
void Max(int a,int b,int c) {}
void Max() {}
void Max(char a,char b)
Max(int a,int b) {}

this关键字

在类的方法定义中使用this关键字可以代表该方法的对象的引用,它是new出来的对象中指向对象自身的关键字。当必须指出当前所使用方法的对象是谁时需要使用this,使用this还可以避免成员变量和形参重名的问题。

public class TestThis {
int i = 100;
TestThis(int i) {
this.i = i; //i为形参i(就近原则),this.i代表的是对象中的i,即成员变量i
//this在此避免了成员变量和形参同名的问题
} TestThis increment() {
++i; //由tt.increment()调用,因此i是成员变量
return this; //返回的this代表TestThis类的对象自身
//this在此表示对象自身
} void print() {
System.out.println("i=" + i);
} public static void main(String[] args) {
TestThis tt = new TestThis(10);
System.out.println("i= " + tt.i);
tt.increment().increment().print();
}
}

上述示例中,new TestThis创建了一个TestThis对象,tt指向该对象。tt.increment()表示调用一次tt对象的方法,此时i自增一次,并返回this,this代表的对象正是tt指向的对象,是TestThis类的对象,所以他也有increment()方法,所以还可以继续执行increment()方法,最后再次返回this,最后执行print()方法,输出自增两次后的i值。

注意,虽然可以return this来返回自身对象的引用,但却不能使用return super来返回父类对象的引用。也就是说,父类对象只能操作其内某个成员,不能直接返回父对象整体。

static关键字

static声明的成员变量为静态成员变量,它是该类的共享变量。在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。

可以通过对象引用或直接通过类名来引用静态成员。即使在没有new出任何对象时,也能直接引用静态成员,因为它属于类。假如类名为T,静态成员变量有i,静态方法有m(),则可以直接使用T.i和T.m()分别引用。当new出来T的一个对象t时,可以使用t.i或t.m(),这和使用T.i和T.m()是完全等价的。(实际上,在不产生冲突的情况下,即使不写类名也可以直接引用静态变量、静态方法)

用static声明的方法为静态方法,静态方法不是针对某个对象来调用的。在调用静态方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员,即静态方法中不可以访问非静态成员变量和其他非静态方法。换句话说,因为静态方法属于类,静态方法看不到heap中各对象中的成员,它只能看到data segment中的静态成员。

public class Student {
static int cnt = 0; //static成员变量
int id;
String name; Student(String name) {
id = ++cnt;
this.name = name;
} public void info() {
System.out.println("id=" + id + ", name=" + name);
} public static void main(String[] args) {
Student.cnt = 100; //在new之前就可以使用类名直接引用static成员变量并赋值
//还可以直接写为"cnt = 100;"
Student s1 = new Student("Malongshuai");
Student s2 = new Student("Gaoxiaofang");
s1.info();
s2.info();
}
}

在上面的例子中,静态变量cnt使用类名直接访问,并使用静态变量cnt作为成员变量id的赋值基础("id=++cnt;")。由于静态变量只在最初进行了赋值,后续一直都通过自增的方式进行改变,这是静态变量的广为使用的功能:"充当计数器"

如果把上面的static关键字去掉,并注释Student.cnt行,再编译运行,那么id的输出结果将总是1,因为cnt作为成员变量被初始化,所有对象的cnt都一样,从而导致id的值也一样。

无论是静态变量还是静态方法,它们都可以在new出对象之前直接引用,这时还不存在对象,因此在静态方法中无法使用非静态的成员变量(它们还不存在)。正如上面的public static void main(),它是静态的,可以直接引用cnt,它不需要在运行时先去new一个对象才能执行,否则main就太"不智能",每次执行都要先new出对象。

如果将static关键字去掉,在编译时将报如下错误:

λ javac Student.java
Student.java:16: 错误: 无法从静态上下文中引用非静态 变量 cnt
Student.cnt=100;
^
1 个错误

继承

类与类之间能体现"什么是什么"的语义逻辑,就能实现类的继承。例如,猫是动物,那么猫类可以继承动物类,而猫类称为子类,动物类称为父类。

子类继承父类后,子类就具有了父类所有的成员,包括成员变量、方法。实际上在内存中,new子类对象时,heap中划分了一部分区域存放从父类继承来的属性。例如,new parent得到的区域A,new child得到的区域B,区域A在区域B中。

子对象中之所以包含父对象,是因为在new子对象的时候,首先调用子类构造方法构造子对象,在开始构造子对象的时候又首先调用父类构造方法构造父对象。也就是说,在形成子对象之前,总是先形成父对象,然后再慢慢的补充子对象中自有的属性。具体内容见"继承时构造方法的重写super()"。

子类不仅具有父类的成员,还具有自己独有的成员,例如有自己的方法、自己的成员变量。子类、父类中的成员名称不同时这很容易理解,但它们也可能是同名的。如果子类中有和父类继承的同名方法,例如父类有eat()方法,子类也有eat()方法,则这可能是方法的重写(见下文)。如果子类中的成员变量和父类的成员变量同名,则它们是相互独立的,例如父类有name属性,子类还自己定义了一个name属性,这是允许的,因为可以分别使用this和super来调用它们。

继承类时使用extends关键字。继承时,java只允许从一个父类继承。

class Person  {
String name;
int age; void eat() { System.out.println("eating...");}
void sleep() {System.out.println("sleep...");}
} class Student extends Person {
int studentID; Student(int id,String name,int age) {
this.name = name;
this.age = age;
this.studentID = id;
} void study() {System.out.println("studing...");}
} public class Inherit {
public static void main(String[] args) {
Student s1 = new Student(1,"Malongshuai",23);
System.out.println(s1.studentID+","+s1.name+","+s1.age);
s1.eat();
s1.sleep();
s1.study();
}
}

注:若您觉得这篇文章还不错请点击右下角推荐,您的支持能激发作者更大的写作热情,非常感谢!

java面向对象基础(一)的更多相关文章

  1. 【重走Android之路】【Java面向对象基础(三)】面向对象思想

    [重走Android之路][基础篇(三)][Java面向对象基础]面向对象思想   1 面向对象的WWH   1.1 What--什么是面向对象         首先,要理解“对象”.在Thinkin ...

  2. 【重走Android之路】【Java面向对象基础(二)】细说String、StringBuffer和StringBuilder

    [重走Android之路][基础篇(二)][Java面向对象基础]细说String.StringBuffer和StringBuilder   1.String String是Java中的一个final ...

  3. 【重走Android之路】【Java面向对象基础(一)】数据类型与运算符

    [重走Android之路][基础篇(一)][Java面向对象基础]数据类型与运算符   1.数据类型介绍 在Java中,数据类型分为两种:基本数据类型和引用类型. 基本数据类型共8种,见下表: 基本数 ...

  4. 086 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 03 面向对象基础总结 01 面向对象基础(类和对象)总结

    086 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 03 面向对象基础总结 01 面向对象基础(类和对象)总结 本文知识点:面向对象基础(类和对象)总结 说明 ...

  5. 085 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 04 构造方法调用

    085 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 04 构造方法调用 本文知识点:构造方法调用 说明:因为时间紧张,本人写博客过程中只是 ...

  6. 084 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 03 构造方法-this关键字

    084 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 03 构造方法-this关键字 本文知识点:构造方法-this关键字 说明:因为时间紧 ...

  7. 083 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 02 构造方法-带参构造方法

    083 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 02 构造方法-带参构造方法 本文知识点:构造方法-带参构造方法 说明:因为时间紧张, ...

  8. 082 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 01 构造方法-无参构造方法

    082 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 01 构造方法-无参构造方法 本文知识点:构造方法-无参构造方法 说明:因为时间紧张, ...

  9. 081 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 06 new关键字

    081 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 06 new关键字 本文知识点:new关键字 说明:因为时间紧张,本人写博客过程中只是 ...

  10. 080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则

    080 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 05 单一职责原则 本文知识点:单一职责原则 说明:因为时间紧张,本人写博客过程中只是 ...

随机推荐

  1. 【读书笔记】【深入理解ES6】#13-用模块封装代码

    什么是模块 模块是自动运行在严格模式下并且没有办法退出运行的 JavaScript 代码. 在模块顶部创建的变量不会自动被添加到全局变量作用域,这个变量仅在模块的顶级作用域中存在,而且模块必须导出一些 ...

  2. LeetCode第[4]题(Java):Median of Two Sorted Arrays 标签:Array

    题目难度:hard There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median ...

  3. 【.Net边角料系列】1-单例模式(我真不是你想的那样)

    什么事边角料? 边角料就是你编程的时候,很少能够用上,或者说你压根就不知道得东西,我就称这些东西为边角料.这个叫.net边角料可能有点大,其实这个系列是纯粹的C#边角料系列. 为什么写.net边角料呢 ...

  4. 做了一个web版的 MyBatis Generator

    mybatis 官方提供了 MyBatis Generator ,可以通过 xml 配置文件的方式使用,例如自己写调用脚本,或者使用 mvn 插件的方式,其实实现起来还是很简单的.虽然简单,但还是不够 ...

  5. AdaBoost入门

    写一点自己理解的AdaBoost,然后再贴上面试过程中被问到的相关问题.按照以下目录展开. 当然,也可以去我的博客上看 Boosting提升算法 AdaBoost 原理理解 实例 算法流程 公式推导 ...

  6. 微信小程序——Now you can provide attr "wx:key" for a "wx:for" to improve performance.

    在官方的swiper(滑块视图容器)中demo代码,运行时会出现Now you can provide attr "wx:key" for a "wx:for" ...

  7. 七牛php-sdk使用-在线打包

    如果需要将空间中的多个文件,打包成一个压缩文件,该怎么做,不需要自己本地打包好再上传,七牛已经为我们提供了这项服务. 命令:mkzip/2/url/xx/alias/xxx; 不仅可以将文件打包,还可 ...

  8. 一个标准的WebView示例

    xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= ...

  9. Java入门篇(三)——Java流程控制

    前两篇已经了解了Java语言基础,本篇开始Java的流程控制.流程控制对任何一门编程语言都是至关重要的,它提供了控制程序步骤的基本手段. 一.复合语句 Java语言的复合语句是以整个块区为单位的语句, ...

  10. python面向对象进阶

    前言 上节大话python面向对象对面向对象有了一些了解,这次就不用大话风格了 (ps:真心不好扯啊) isinstance与issubclass isinstance(obj,cls)检查是否obj ...