初识Java语言- 类和对象
Posted 飞人01_01
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识Java语言- 类和对象相关的知识,希望对你有一定的参考价值。
初识java语言(四)- 类和对象
文章目录
往期文章:
一、类与对象的概念
1、类
类(class)是构造对象的模板或蓝图。
我们可以将类(class)想象成一个模具,将对象想象成一个由模具形成的物品。如下:
-
封装(encapsulation,有时称为数据隐藏)是处理对象的一个重要概念。
从形式上来看,封装就是将数据和行为(方法)组合在一个包中,并对对象的使用者隐藏了具体的实现方式。实现封装的关键在于,绝对不能让类中的方法直接访问其他类的实例字段(成员变量)。
-
通过扩展一个类来建立另外的一个类的过程称为继承。
有关继承的细节问题,我们后面再将。记住 : 在java中,所有的类源自一个“超类”,也就是Object
。
2、对象
面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。对于一些规模较小的问题,将其分解为过程的开发方式比较理想(面向过程)。面向对象更加适合解决规模比较大的问题。
要想使用面向对象程序设计,一定要清楚对象的三个特性:
- 对象的行为(behavior)----- 可以对对象完成哪些操作,或者是应用哪些方法?
- 对象的状态(state)----- 当调用这些方法时,对象会如何响应?
- 对象的标识(identity)----- 如何区分具有相同 行为与状态的不同对象?
3、面向过程与面向对象的区别
面向过程:
- 系统以过程方法为中心来组织
- 过程间相互发送数据
- 过程的执行动作与数据被明显的分离
- 关注的焦点在于数据结构、算法和执行步骤
- 过程通常难以被复用。
面向对象:
- 系统以对象为中心来组织
- 对象相互间发送消息
- 相关的属性和行为被统一到对象上
- 关注的焦点是对象及对象的职责
- 使得系统构建更容易,易维护,易扩展,易复用
- 解决问题的方式更接近人的
举个例子: 假设你现在需要洗一堆衣服
面向过程: 拿盆 -> 加水 -> 放洗衣粉 -> 手洗 -> 再加水 -> 再洗一遍 -> …… -> 晾晒 -> 完成
面向对象: 只需要三个对象: 人 、 衣服 、 洗衣机; 我们将衣服放入洗衣机,放入洗衣服,启动洗衣机即可。在这三个对象里分别写一些方 法: 比如洗衣机 :自动加水,换水等等。
二、类的成员
1、字段
对象中的数据称为实例字段(成员变量)。
作为一个类的实例,特定对象都有一组特定的实例字段值,这些值的集合就是这个对象当前状态。无论何时,调用对象上的方法(函数),都有可能改变它的状态。
//单链表结点
class Node {
public int val; //字段或者 成员变量
public Node next;
}
有时候,我们需要设置成员变量的初始值,也可以这样完成操作。(显式设定初始值)
class Node {
public int val = 10;
public Node next = null;
}
2、方法
就是我上一篇文章讲过的方法,是一样的。用于描述对象的行为。Java中方法的概念以及递归的讨论
class Node {
public int val; //字段
public Node next;
//方法
public void show() {
System.out.println(this.val);
}
}
public class Main {
public static void main(String[] args) {
Node node = new Node(10);
node.show(); //调用类中的方法
}
}
3、static关键字
- 修饰属性
- 修饰方法
- 修饰代码块
- 修饰类(在后面的文章会具体讲解)
修饰属性: Java静态属性和类相关,和具体的实例无关。换句话说,同一个类的不同实例共用同一个静态属性。
class Test {
public int val;
public static int count;
}
public class Main {
public static void main(String[] args) {
Test demo1 = new Test();
demo1.count = 10; //为count进行初始化
System.out.println("调用demo1时 " + demo1.count);
demo1.count++;
System.out.println("============");
Test demo2 = new Test();
System.out.println("调用demo2时 " + demo2.count);
}
}
运行结果:
是不是感到很疑惑? 我们来画一画这个内存图,就能理解其中的奥妙了!!!
上诉代码内存图:
由图可知,我们虽然创建了两个实例化对象(demo1和demo2),但是因为类中的实例字段(count)被static修饰,所以它是在方法区开辟内存空间,且接下来由这个类所实例化的所有对象里面的count,都是指向方法区count所开辟的一块空间。(说白了,就是count的空间被所有对象所共享,一个对象将count改了,其他对象输出的count也是被改了的)
修饰方法: 如果在任何方法上应用static
关键字,则这个方法称为静态方法。
- 静态方法属于类,而不属于对象。
- 可以直接调用静态方法,而无需创建类的实例。
- 静态方法可以直接访问静态数据成员,并可以修改静态数据成员的值。
class TestDemo {
public int val;
public static int count;
public static void change() {
//val = 100; //error,静态方法不能访问非静态成员。原因看上面的三点
count = 100;
}
}
public class Main {
public static void main(String[] args) {
TestDemo.change;
System.out.println(TestDemo.count);
}
}
因为change
方法是静态的,所以不需要进行实例化,就可以直接通过类名进行调用。
注意事项1: 静态方法和实例无关,而是和类相关。因此这导致了两个情况:
- 静态方法不能直接使用非静态数据成员或调用非静态方法。(因为非静态数据成员和方法都是和实例相关的)。
this
和super
两个关键字不能在静态上下文中使用。(this是当前实例的引用,super是当前实例父类实例的引用,也是和当前实例相关的)- static不允许修饰局部变量。局部变量不是类中的变量,而是方法中的。
注意事项2:
- 实际上一个方法具体要不要带static关键字,都是需要看情况的。
- main方法是被static修饰的!!!
修饰代码块:
java中初始化实例字段的方法有三种:
- 在构造方法中设置值
- 在声明中赋值
- 用初始化块设置值
四种代码块:
1. 本地代码块(定义在方法里面的)
2. 实例代码块(构造代码块)
3. 静态代码块
4. 同步代码块
在一个类的声明中,可以包含任意多个代码块。只要构造这个类的对象,这些代码块就会执行。也就是是初始化块里的数据,是为了构造对象时,做一些准备工作。例如:
初始化块,放在前面或者后面,都没关系。因为实例化时,首先会将这些初始化块,从上至下的执行一次。然后才是执行构造方法的的语句。
如果类的静态字段需要很复杂的初始化代码,那么就可以使用静态的初始化块。将代码放在一个块中,并标记关键字static
。
看图,上面的代码。先是执行的静态代码块->实例代码块->构造方法
,且静态代码块只会在第一次使用这个类的时候初始化这一次,看下图:
代码块与实例字段初始化的顺序:
总结: 在类第一次加载的时候,将会进行静态字段的初始化。与实例字段一样,除非将静态字段显式地设置成其他值,否则默认的初始值是0、false或者null。所有的静态字段初始化方法以及静态初始化块都将依照类声明中出现的顺序执行。
三、自定义类
1、从构造方法开始
在上(类的成员)文中,我们提到了类中实例字段的初始化,说到了显式初始化字段值。除了这样初始化,Java还提供了一个叫构造方法的功能。专门用于实例化对象时用。上图所示,就是一些构造方法。
书写格式: public 类名() {}
在构造ListNode类的对象时,会调用构造方法,从而将实例字段初始化为所希望的初始状态。下面三种初始化都可以:
ListNode node1 = new ListNode(); //无参构造
ListNode node2 = new ListNode(20); //一个参数的构造
ListNode node3 = new ListNode(20, null); //两个参数的构造
构造方法与其他方法有一个重要的不同之处。构造方法总是结合new运算符来调用。不能对已经存在的对象调用构造方法来达到重新设置实例字段的目的。
构造方法需要记住这几个点:
- 构造方法与类同名
- 每个类都有一个以上的构造方法。(没写构造方法的,编译器会默认自带一个无参构造方法)
- 构造方法可以有0个、1个或多个参数
- 构造方法没有返回值
- 构造方法总是伴随着new操作符一起调用
注:
我们上面说过,如果自己不写构造方法,编译器会默认自带一个无参构造方法。但是:
如果我们自己写了一个有参构造方法,没写无参构造方法。 此时如果我们实例化对象,调用无参构造方法,就会报错。因为编译器默认带无参构造方法,是因为类中没写任何的构造方法,才会自带无参构造。
要记住所有的Java对象都是在堆中构造的。
2、用var声明局部变量
在Java10中,如果可以从变量的初始值推导出它们的类型,那么就可以用var
关键字声明局部变量,而无需指定类型。例如,可以不用这样声明:
ListNode node = new ListNode();
只需写一下代码即可:
var node = new ListNode();
这样就可以避免重复写类型名ListNode。
从现在开始,倘若无须了解任何Java API就能从等号右边明显看出类型,在这种情况下,我们都将使用var表示法。不过,我们不会对数值类型使用var,如int、long、double等,使你不用当心0、0L、0.0之间的区别。
注意var关键字只能用于方法中的局部变量。参数和字段的类型必须明确声明。
3、显式参数与隐式参数
我们先看一段代码:
reDouble方法,有两个参数。第一个参数salary称为隐式参数,是出现在方法名前的TestDemo类型的对象。第二个参数是位于方法名后面括号中的数值,这是一个显式参数。
可以看到,显式参数显式地列在方法声明中,例如 int num
, 隐式参数的声明没有出现在方法中。
在没一个方法中,关键字this指示隐式参数。this代表 指向当前对象的引用。
注释: 在C++中,通常在类的外面定义方法:
void TestDemo : : reDouble(int num) {} //C++
如果在类的内部定义方法,这个方法将自动成为内联方法。
class TestDemo
{
int reDouble(int num){} //inline in C ++
}
但是在java中,所有的方法都必须在类的内部定义,但并不表示它们是内联方法。是否将某个方法设置为内联方法是java虚拟机的任务。编译器及时监视那些简短、经常调用而且没有被覆盖的方法调用,并进行优化。
4、认识引用
-
引用一定只能在栈上吗?
答:不一定。比如创建一个数组,数组的每个元素都是引用数据类型,此时数组本身是在堆上开辟的空间。这些引用就存储在堆上。
-
引用能指向引用吗?
答:不能。引用只能指向对象。
-
一个引用能指向多个对象吗?
答: 不可以。比如一个Person类型的引用变量 per ,再多次new新对象,且每次将返回的地址赋值给per时,最终per还是只能指向最后一次赋值的对象的地址。
-
一个引用赋值null代表什么。
答:代表当前引用不指向任何对象。也不指向任何一块内存空间。与C语言不一样。
本期更新就到此结束啦,朋友们下期见!!!
以上是关于初识Java语言- 类和对象的主要内容,如果未能解决你的问题,请参考以下文章