目录
导言
我们学过java应该清楚java中存在基本数据类型,以及引用数据类型。
基本数据类型指的是直接比较值的数据类型,而引用数据类型由于存放的是内存地址无法直接比较内部要素,只能比较内存地址,而类也是一种引用数据类型。
可大家有没有想过,为什么java要选择类这个概念?比较与java的前辈C语言面向过程,java为何要选择面向对象呢?
类以及对象的概念
什么是类呢?简单来说,类是一种拥有共同性质的属性以及方法的集合。如何来理解这句话?
在现实中,人们为了归纳总结现实中具有共同性质的物体或者感念,会讲其进行抽象---将不同的事物的特征抽离出来,忽略其他不同将具有相同的的特征的事物放在一起,并冠以相同的名称。已达到整理的作用,如《沙丘》和《三体》,虽然这两本书讲的故事不同,甚至作者不同,但是本质上都是对科学的思考和幻想,所以我们可以为这一类书冠以 科幻小说 。不代表具体的书,而是描述其特征,我们可以更高效的描述一些事物。这就是类,这也是Java选择类的原因之一。
再做个比喻,我们虽然有着不同的名字,不同的的外貌,但我们都属于人类着一个类当中,我们都有着性别、年龄、基因等等相同特征,我可以这么说:“我是一个18岁的男孩子。”虽然我没有告诉你我是谁,我长什么样子,但是根据这些描述您能大概知道我是个人且是个男人。性别、年龄、基因这些描述就是java中的类的属性---用于描述类的特征的数据类型。而我们都会吃饭、睡觉、工作或者思考问题,这些行为解释了为什么我们是人类。这行为就是java中类的方法---用于完成类的的某种行为的代码块。
那面向对象中的对象是什么意思呢?
这还得回归到现实中来,我们如何在人群中定位到我们自己?我们就要尽可能的详细的描述自己了,我可以说:“我是xxx,已经练习唱跳2年半了,擅长唱跳,rap篮球。”然后来一段唱跳。这些不同将我从人类这一大类中抽离出来,重新成为了一个具体的人。这种从抽象集合中,抽出来具体的个例就是对象。在Java中满足所属类型数据结构的具体数据就是对象。
现在再来回顾下我们的问题,为什么java要采取面向对象的方式?
第一,面向对象的思想更符合我们现实中的思考方式。我们为了方便通常都不会特指多个实际的事物,而是通过描述一类事物进行表达,就像刚刚我说的“事物”。再根据实际的情况下,再进行具体事物的描写。正因如此,java相比其他面向过程的高级语言有着更容易上手,代码的可读性更高。
第二,代码易于维护。由于我们将具有相同功能性质的代码归纳封装成了一个类,所以我们在一个功能出现错误的后,直接前往负责的类进行维护,排除bug。
第三,代码易于扩展。由于我们将代码封装成类。使得类中的代码可以更专注于自身所负责的任务-----这就是代码的内聚。这也使得不同类之间的依赖关系变小,其中的依赖关系就是---代码的----耦合,最直接的表现就是,当一个类中代码出错时,一般只用修改那个类中出错的地方即可。这也是代码中所追求的高内聚,低耦合。
第四,代码复用率高。由于类是完成一类功能的代码块,所以我们可以将类看成是一个模板。当我们在不同程序内需要一种功能时,我们可以使用同一类代码完成,这大大节约了存储空间,也减少了程序员的工作量,将代码变得更简单,优美。
我们回到java中,面向对象的表现在将拥有相同性质的代码封装为类。并将描述类的代码称为属性(类比人的名字或年龄),将完成类的行为的代码称为方法或者函数。
我们可以使用 class 关键字进行类的定义。
//类的定义
class 类名 {
//成员属性
public int i;
//成员方法
public void 方法名(参数){
方法体
}
}
//例子
class Example{
//成员属性
public String a;
//成员方法
public String getA(){
return a
}
}
new 关键字
当我们需要创建对象时就需要 new 关键字配合构造函数进行创建。
//创建对象
类名 成员名 = new 类名(参数);
//例子
Integer i = new Integer();
继承
大家可以想想,我们虽然是人类,但是我们还是在动物的范畴中,这时人类就是动物这一大类中。人类以及动物之间就构成了继承。
在java中我们将Animal包含了小类的大类成为父类,人类这种小类被称为子类。子类与父类是一种相对关系,就像您称自己的父亲叫爸爸,您父亲叫您儿子,但您的祖父教您的父亲叫儿子,您的父亲称您的祖父叫爷爷。
子类可以不用额外声明父类中已经有的属性和方法。并可以调用父类的部分方法以及属性。
extend 关键字
在java中,我们使用 extends 关键字表示这种继承关系。
class Human extends Animal {
//子类将继承父类的属性以及方法
}
class Animal{
public int age
public growUp(){
}
}
this 与 super 关键字
this关键字表示本类对象,super表示父类对象。是对重名元素进行区分。好比你的,我的。
我们可以通过 this.对象元素 或者super.对象元素 的方式调用本类或者父类允许访问的方法和属性。参数名与对象中的属性名称重名时将可以使用 this 关键字代指。
class Parent{
public int x;
public void f1(){
System.out.println("this is f1()");
}
}
class Child extends Parent{
public int a;
public void setA(int a){
this.a=a;
}
void f(){
super.f1();
System.out.println("Parent.x"+super.x+",Child.a"+this.a);
}
}
我们也可以使用 this(参数) 或者 super(参数) 的方式调用本类或者父类对应参数的构造函数。不过值得注意的是,这类使用方式我们必须将其放在构造函数内,并且我们需要将其放在构造函数方法体内部第一行
class Parent{
public Parent(int i){
}
}
class Child extends Parent{
public Child(){
super(1);//将调用父类Parent中的Parent(int i)这个构造方法。
//其他代码
}
public Child(){
this();//将调用本类中Child()这个构造方法。
//其他代码
}
}
注意
当对象内并未存在重名问题时,将可以省略this和super直接使用所需的元素。
对于重写的方法内部使用super调用父类中被重写的同名方法时,调用的是父类的同名方法,正在进行的重写并不会对父类方法进行影响。
this和super使用目标都是对象,当我们直接绕过对象使用类的方法以及属性时,将不能使用this和super关键字。如静态方法中。
权限修饰符
在现实生活中,我们都希望自己有些事情不让别人知道,或者只让一些人知道这些事,或者只对某些人才做的事,比如您的房子只给你认识的人进入,您的名字只给认识的人知道。
java中如果类中的属性或者方法不想让其他类随意访问,就需要 权限修饰符 进行限制了。
我们可以在类和成员声明前添加关键字进行修饰。表示这类代码块的访问级别,以下是权限修饰符关键字:
public class{
int i;
private int j;
protected setJ(int _j){
j=_j;
}
}
public
可以用于修饰类与成员。
能访问修饰的代码的类:
- 自己
- 子类
- 同一包中的类
- 其他类
不能访问修饰的代码的类:
无
protected
可以用于修饰成员。
能访问修饰的代码的类:
- 自己
- 子类
- 同一包中的类
不能访问修饰的代码的类:
其他类
无修饰符(default,默认)
可以用于修饰类与成员
能访问修饰的代码的类:
- 自己
- 同一包中的类
不能访问修饰的代码的类:
- 子类
- 其他类
private
可以用于修饰成员。
能访问修饰的代码的类:
- 自己
不能访问修饰的代码的类:
- 子类
- 其他类
- 同一包中的类
final 修饰符
final修饰符表示最终的含义。我们可以使用 final 修饰类,属性以及方法。修饰的对象不同final的方法也不同。
修饰类时
该类将无法被继承。
修饰属性时
该属性需要被初始化,且初始化后将无法被修改。所以我们也可以将被修饰的属性称为常量。
值得注意的是对于被final修饰的引用类型常量时,由于成员属性记录的是引用对象的内存地址,所以对于引用的对象其中允许修改的属性是不会收到影响的。
修饰方法时
该方法将不能被子类重写。
static 修饰符
static 修饰符表示静态的含义。我们可以使用 static 修饰属性以及方法。
使用 static 修饰的代码将属于类。在类被加载时,static修饰的代码将会被一并加载并只加载一次。由于修饰的代码属于类,则无法使用 this 这类指代对象的关键字以及this的隐式用法。所以当修饰方法时,方法只能使用静态变量。
abstract 修饰符
abstract 修饰符表示抽象的含义。我们可以使用 abstract 修饰类以及方法。
//抽象类格式
abstract class 类名{
//抽象方法格式
权限修饰符 abstract 返回值 方法名(参数);
}
//例子
abstract class 类名{
public abstract void f1();
}
修饰类时
该类将被视为抽象类,允许出现抽象方法。该类将无法创建对象,通过子类实现方法后才能创建对象。
修饰方法时
该方法将被视为抽象方法,该方法将不需要方法体,且是未被实现的状态,无法直接使用。
接口(Interface)
接口是一种特殊的抽象类。在接口中,只能出现公共的抽象方法,且不需要关键字修饰方法。
接口表示的是一种规范,就像插头,在中国一般就只有两种插头与插座匹配就是2脚或3脚的。这个插头规范就是Java中的接口。
//接口格式
interface 接口名{
返回值 抽象方法(参数);
}
//例子
interface MyInterface{
void f1();
Integer f2(int i);
}
implements关键字
我们要实现接口我们就需要借用 implements 关键字进行描述。
class 类名 implements 接口1,接口2....{
//实现接口的抽象方法
}
继承的关键字 extend 类似,但不同的是,java中支持多接口实现 。而继承只能继承一个父类。
接口的继承
接口可以继承接口。
实现接口的方法将需要实现该接口以及该接口的父类的接口。
//例子
public interface MyInterface extend MyInterface0{
void t1();
}
interface MyInterface0{
void t0();
}
class Test implemnts MyInterface{
public void t1(){
System.out.println("这是t1");
}
public void t0(){
System.out.println("这是t0");
}
}
匿名类
这是我们后续将要经常用到的一种抽象类和接口实现方式。
一般来说我们实现抽象类和接口时都会进行类的创建,通然后继承抽象类并实现抽象方法或者实现接口的方式实现抽象类以及接口。但是每一次实现我们都将创建一个具体的类这将大大增加我们写代码的工作量。所以我们将采取匿名类的方式进行实现。
//格式
抽象类名/接口名 引用变量名称 = new 抽象类名/接口名(){
//在此处实现抽象方法。
}
//例子
interface A{
void f1();
}
class B{
public A a=new A(){
@Override //重写注释,这是给java虚拟机看的。可以不写
f1(){
System.out.println("f1()");
}
}
}
上面的代码中我们并没有为接口 A 单独声明一个类,但是通过匿名类的方式,通过直接实现接口的方式创建了一个对象。这大大我们的时间也使得代码更加整洁易懂。
补充
其他常用关键字
- package(包):用来表示该公共类所处的位置。使用 . 进行分割,最后一块为公共类名。
- import(包含):用来表示包含引用类,注意要用全名,对于其他包内类使用,我们需要import进行表明,使用规则和类似package,但是我们可以用 . 后 * 表示 该包内所有类。
- null (空对象 或 指针) : 这是引用变量的默认值,表示没有引用任何对象。若我们使用空对象的属性是将报出异常。.
未完待续。。