3. 类与对象
3.1 面向对象编程三特性:
-
封装
-
继承
-
多态
3.2 类的编写
java是面向对象语言,类是基本要素,一个java程序就是由若干类组成。
何为对象?对象就是用类声明的变量。
如何编写类是编写java程序的基础,接下来让我们看看如何编写一个java类叭。
-
编写格式:
class 类名{ // 类体 }
-
类名规则(非语法要求,但应当遵守):
- 如果类名使用英文,首字母应当大写,如:People,Timer.
- 易识别,见名知意。
-
类体
-
属性 : 在类中声明的成员变量。
class People{ int height;// 身高 int weight;// 体重 }
成员变量在整个类中都有效,与书写位置无关。
成员变量可以是java中任何一种数据类型。
-
方法 : 在类中声明的成员函数。
class People{ //人具有说话行为,说话就是人这个类的方法。 void speak(){ System.out.println("I'm speaking!"); } // 睡觉 void sleep(){ System.out.println("I will sleep!"); } }
注意:
-
成员变量和局部变量区别:
-
局部变量就是定义在方法内的变量。
-
成员变量有默认值(int:0,float:0.0,boolean: false,引用型: null),局部变量没有。
-
重名时,成员变量失效。如果需要使用成员变量,需要用this关键字:this.成员变量。
-
-
类由变量的声明和方法的定义组成,对变量的操作得在方法中。
class A{ int a; a=23; //错误 void f(){ a=23; //正确 } }
-
-
-
-
构造方法:
构造方法,是创建对象的时候,自动运行的方法。
如果没写构造方法,系统会调用默认的构造方法,默认构造方法无参数,方法体没有语句。
书写规则:
- 无类型
- 必须和类名同名
- 可以拥有多个,但需保证参数不同
class A{ int x; //构造方法1 A(){ System.out.println("this is constructor method!"); } //构造方法2 A(String s){ System.out.println(s); } // 有类型,虽然和类名同名但不是构造方法, void A(){ System.out.println("this is't constructor method!"); } }
3. 3 创建对象
当我们编写好类之后,就可以创建对象了,与创建变量类似。
-
声明对象:
类名 对象名字;
class A{ int a; } // 声明对象,此时a中的内存无任何数据,称a为空对象 A a;
-
分配变量:
对象名字 = new 类名();
a = new A();
-
声明并分配变量:
类名 对象名字 = new 类名();
A a = new A();
3.4 使用对象
创建好对象之后,对象中有属性和方法,如何通过对象访问属性或运行方法呢?
- 通过点(.)运算符:
- 访问属性: 对象.属性。
- 运行方法:对象.方法()。
注意:
-
避免使用空对象。
-
允许同一类不同对象之间赋值,赋值后就拥有相同的引用。
例如:
class People{ double height; //身高 double weight; //体重 } public class Example{ public static void main (String[] args){ People p1=new People(); People p2=new People(); p2=p1; } }
内存模型:
- p2=p1 前:
+ p2=p1 后:
- 当赋值后,p1和p2都具有引用1,修改任意一个,另一个也会对应修改,如:
class People{
double height; //身高
double weight; //体重
}
public class Example{
public static void main (String[] args){
People p1=new People();
People p2=new People();
p2=p1;
//修改p1.height,那么p2.height也会被修改
p1.height=170;
//输出p2.height
System.out.println(p2.height);//=>170
}
}
-
垃圾收集
- 上面的p1赋值p2之后,p2获得了p1的引用,p2和p1拥有同样的变量,那么p2原本的变量呢?系统发现p2原本在内存中的变量不再被p2或者其他对象引用时,就会释放掉。这就是java的垃圾回收机制。因为垃圾回收机制的存在,java很少出现内存泄漏,java类也只需要有构造方法,而不需要像c++那样写一个析构方法来释放不用的变量。
3.5 程序和类基本结构
java应用程序(java工程)由若干类构成,这些类可以存在一个源文件中,也可以分布在不同的源文件中。
只有一个含有main方法的主类,mian方法是程序执行入口。
类与程序结构:
编写好代码,程序运行流程:
注意:
1. 如果应用程序的主类源文件和其他源文件在同一目录,只需要编译主类源文件,其他源文件会自动被编译。
案例:
书写三个类:矩形,梯形,主类。矩形类和梯形类分别写在Rect.java、Lader.java源文件中,主类写在Example.java中。主类创建矩形对象,梯形对象,并输出面积。
-
矩形类
- UML图:
-
代码:
public class Rect{ double width; double height; double getArea(){ return width*height; } }
-
梯形类
- UML图:
-
代码:
public class Lader{ double above; double height; double bottom; double getArea(){ return (above+bottom)*height/2; } }
-
主类
-
代码:
public class Example{ public static void main(String[] args){ Rect rect = new Rect(); Lader lader = new Lader(); rect.height = 10.2; rect.width = 12.56; lader.above = 2.3; lader.bottom = 3.4; lader.height = 10.3; System.out.println("rect'area :"+rect.getArea()); System.out.println("lader'area :"+lader.getArea()); } }
-
-
编译
- 应用程序三个源文件处于同一个文件夹,只需要编译主类源文件,其他自动编译:javac Example.java
-
解释执行
- java Example
提示:
1. 如果要编译某个目录下所有源文件,可以用通配符(*): javac *.java
1. 虽然java源文件可以有很多类,但提倡一个源文件只写一个类。这样有利系统的维护,假设需要修改某个类,只需要找到对应的源文件修改重新编译即可,而不用在一个源文件中到处找,然后又得整个文件重新编译。
3.6 参数传值
类中很多方法,都会有自己的参数(局部变量),当对象调用方法时,参数就会被分配内存,得向参数传值。所以了解方法的参数传值机制是必不可少的。
-
基本类型传值:
- 低–>高(✔️)高–>低(✖️)
- 级别低向级别高传值,会默认转换了,高级别向低级别不行,得强制转换。
- 如:float向double传值可以,double向float不可以,因为double比float级别高
- 低–>高(✔️)高–>低(✖️)
-
引用类型传值:
java引用类型数据:数组,对象,接口(后面会学)
引用类型变量存放的是引用(类似c语言中的指针),而不是变量应用的实体。
所以当参数是引用类型的时候,传递的是引用,而当两个引用型变量拥有同样的引用的时候,就会拥有同样的实体,改变其中一个,另一个也随之改变,如上面的使用对象一节原理相同!
3.7 可变参数
有时候我们书写某些方法的时候,这个方法的参数个数可以不固定,可能是2个、3个、6个等等。这时候我们就需要学习如何给方法书写可变参数了。
-
“···” 三个点表示若干参数,这些参数类型必须相同:方法名(可变参数类型…参数代表){}
public class ChangeArguments { public static void main(String[] args) { Test t = new Test(); t.test(2,3,4); } } class Test{ //可变参数方法 public void test(int ...x){ int sum=0; System.out.print("可变参数:"); for(int i:x){ System.out.print(i+" "); } System.out.println(); System.out.println("可变参数个数:"+x.length); for(int parm:x){ sum+=parm; } System.out.println("可变参数的和:"+sum); } }
输出:
细节:
- 上面的test就是可变参数方法。
- int为可变参数类型。
- x为参数代表,是一个数组。(为什么会有参数代表呢?当传递可变参数的时候,由于个数未知,我们如何去取到传递过来的参数呢?自然得有个东西来存储这些参数,方便我们在函数中接收使用传过来的参数,这个东西就是参数代表,是一个数组)
-
可变参数必须是参数列表中最后一个。
public class ChangeArguments { public static void main(String[] args) { Test t = new Test(); t.test(1.0f,2,3,4); } } class Test{ //可变参数方法 public void test(float y,int ...x){ System.out.println("不可变参数:"+y); } }
- public void test (float y,int …x) {}(✔️)
- public void test (int …x,float y) {}(✖️)
3.8 组合复用
java支持的数据类型都可以创建对象,类也是java的数据类型,当自定义一个类A的时候,在类A中以类B来定义属性,就说A组合B。这个很好理解,int等基本数据类型可以用来定义属性变量,而类也是数据类型,也可以用来定义属性变量。
需要注意的是:类定义的对象是引用型数据,传值时遵循参数传值提到的引用类型传值规则。
案例:
创建:圆类,圆锥类,主类。圆锥类底部是一个圆,就可以使用圆类创建一个圆对象来表示,通过圆对象求得圆锥的底面积。
-
Circle.java
public class Circle { double radius,area; void setRadius(double r){ radius=r; } double getRadius(){ return radius; } double getArea(){ area=3.14*radius*radius; return area; } }
-
Circular.java
public class Circular { Circle bottom; double height; void setBottom(Circle c){ bottom=c; } void setHeight(double h){ this.height=h; } double getVolme(){ if(bottom == null){ return -1; } else{ return bottom.getArea()*height/3.0; } } double getBottomRadius(){ return bottom.getRadius(); } public void setBottomRadius(double r){ bottom.setRadius(r); } }
-
Example.java
public class Example { public static void main(String[] args) { Circle circle = new Circle(); circle.setRadius(10); Circular circular = new Circular(); System.out.println("circle 的引用:"+circle); System.out.println("圆锥的bottom的引用:"+circular.bottom); circular.setHeight(5); circular.setBottom(circle); System.out.println("circle 的引用:"+circle); System.out.println("圆锥的bootom的引用:"+circular.bottom); System.out.println("圆锥的体积:"+circular.getVolme()); System.out.println("修改circle的半径,bottom的半径同样变化"); circle.setRadius(20); System.out.println("bottom的半径:"+circular.getBottomRadius()); System.out.println("重新创建circle,circle的引用将发生变化"); circle = new Circle(); System.out.println("circle的引用:"+circle); System.out.println("但是不影响circular的bottom的引用"); System.out.println("圆锥的bootom的引用:"+circular.bottom); } }
3.9 实例成员和类成员
类体包含:声明成员变量和定义方法。成员变量细分:实例变量和类变量(静态变量);方法细分:实例方法和类方法(静态方法)。
-
成员变量
-
实例变量:声明时最前面没有static修饰。
-
类变量:声明时最前面有static修饰。
class People{ int x; //实例变量 static double z; //类变量 }
当字节码被加载到内存的时候,类没有创建对象的时候,实例变量不会被分配内存,而类变量一开始直接就被分配了内存。当类通过new运算符创建多个对象,这些对象就会被分配到不同的实例变量,类变量不再分配内存,所有对象共享一开始就被分配了内存的类变量。类变量可以通过类名访问,也可以通过对象访问,实例变量只能通过对象访问。
案例:
class People{ int x; static double y; } public class Example { public static void main(String[] args) { People p1 = new People(); People p2 = new People(); p1.x= 4; p1.y=4.6d; System.out.println("p1的实例变量x:"+p1.x); System.out.println("p1的类变量y:"+p1.y); p2.x=8; p2.y=10.3d; System.out.println("p2的实例变量x:"+p2.x); System.out.println("p2的类变量y:"+p2.y); System.out.println("p1的类变量y:"+p1.y); People.y=12.7d; System.out.println("通过类名改变类变量y,p1.y:"+p1.y); System.out.println("通过类名改变类变量y,p2.y:"+p2.y); } }
输出:
-
-
成员方法:
-
实例方法:声明时最前面没有static修饰
-
类方法:声明时最前面有static修饰
class Example{ int a; //实例方法 int max(int b,int c){ return b>c?b:c; } //类方法 static int staticFun(){ System.out.println("hi,this is static function") } }
解析:系统把字节码加载到内存中的时候,实例方法不会被分配入口地址,当该类创建第一个对象的时候,实例方法就会被分配入口地址,可供所有对象共享调用,后面创建的同类多个对象,就不需要再给实例方法分配入口地址,可直接调用,当所有对象不存在时,方法入口地址才被释放。类方法在字节码加载到内存的时候,就会被分配入口,后续不再分配,直到程序结束释放。类方法和类变量一样,也是可以通过类名和对象调用。
注意:
- 类方法可以被类名和对象调用,而实例方法只能被对象调用。
- 类方法不可以操作实例变量,因为类创建对象之前,类方法就存在了,而实例变量还没有被分配内存。
案例:
为什么java要把方法细分出来一个类方法呢?有什么用呢?
当一个方法不需要操控类中任何实例变量,就可以实现需要的效果,我们就可以把这个方法定义为一个类方法。
下面案例让我们体验下类方法的奇妙之处吧。
编写java程序的时候,我们经常需要导入(import)包,实现我们需要的效果,这些包中的方法,我们经常都是直接用的。
比如:
import java.math.*; public Test{ public static void main (String[] args){ //调用max方法比较 1,2大小 Math.max(1,2); } }
上面的Math类的max方法不需要创建Math对象,就可以直接通过类名Math调用,所以max是一个类方法。java很多包提供了很多类方法,再比如。Arrays类的sort方法。
-
3.10 方法重载
当我们已经写了一个方法:int max(int a,int b),用来比较两个整数的大小。如果我们需要比较两个double型的浮点数大小,用max的话,a,b都是int型,不能够传double型数据,这时候我们可以再写一个max方法 : double max(double a,double b),使之可以比较double数据。这时候,max方法就有两种,一种是int型参数,一种是double型参数,当我们调用max方法的时候,系统会检查参数时double型还是int型,是double就调用double max(double a,double b);是int就调用int max(int a,int b),这就可以只调用max方法就可以实现多种功能,这就是方法的重载。
- 方法重载,参数必须不同。
案例:
class A{
int max(int a,int b){
return a>b?a:b;
}
double max(double a,double b){
return a>b?a:b;
}
}
public class Example {
public static void main(String[] args) {
A a = new A();
int c = a.max(1,2);
double d = a.max(1.9d,3.4d);
System.out.println("int型max:"+c);
System.out.println("重载后double型max:"+d);
}
}
输出:
3.11 this关键字
this是java的关键字,表示某个对象。
当我们创建类遇到下面这种情况:
class Example{
String name;
// 上面的name属性和构造方法的name参数同名,这时候我们本来是想把参数name赋值给name属性
// 但是两个同名了,name=name会产生歧义,我们该如何解决这个问题呢。this就登场了。
Example(String name){
this.name=name;
}
// this表明对象本身,如果Example类创建了e1对象,this就是e1;创建了e2对象,this就表明e2。this.name就表明e1.name或者e2.name,这样子就可以把形参name和属性name区分开来了。
}
相信通过上面的例子,你一定体会到this的用处了吧,但是使用this时,需要注意下使用限制~
-
this可以出现在实例方法和构造方法中,不可以出现在类方法中。
为什么不可以出现在类方法中呢?因为类方法是所有对象共享的方法,是唯一的。而this指代的是当前对象,每个对象的this不同,在类方法中出现this,就会导致类方法变得不唯一,不符合类方法的特性。
3.12 访问权限
一个类创建了对象之后,就可以通过点“.”运算符操控自己的变量,调用类中的方法。但对象操控自己的变量和使用方法时,也是有一定的限制,这就是接下来介绍的访问权限。
-
访问权限:对象是否有权限通过“.”运算符去操控自己的变量和调用类中的方法。诶,你看哈,上句说:“是否有权限”,那么如何判断对象是否有权限去去操控自己的变量和调用类中的方法呢?所以我们在编写一个类的时候,就应该给类中的成员变量和方法设置好访问权限。那么如何去设置访问权限呢?这就得用到访问限制修饰符:private,protected和public。
-
private
顾名思义,私有的。所以用private关键字修饰的成员变量和方法,就称为私有变量和私有方法。
例如:下面的a就是私有变量,setA()就是私有方法。
class Pri{ private int a; private void setA(){ a=4; } }
如果设置权限为private了,那么用Pri类创建的对象就不能直接通过"."运算符访问a变量和setA方法了。比如:
class Example{ void g(){ Pri p = new Pri(); p.a=5; //非法,不允许访问私有变量a p.setA(); //非法,不允许调用私有方法setA() } }
-
成员变量分为:实例成员变量和类变量,上面举例用的实例成员变量,但也适用于类变量。在另一个类中,也不能通过Pri类名操作私有类变量。
-
成员方法分为:实例方法和类方法,上面举例用的实例方法,但也适用于类方法。在另一个类中,也不能通过Pri类名调用私有类方法。
疑问:说了这么多,感觉private挺麻烦的,为什么要有private来限制访问权限呢?
当通过某个类在另一个类中创建对象时,有的成员变量我们不希望对象可以直接访问,就可以把这个成员变量访问权限设置为private,然后通过类中的某个方法来操控这个变量,同时面向对象编程也提倡对象应当调用方法来改变自己的属性。比如,我们创建一个People类,People中有年龄(age)属性,在另一个Test类中通过People类创建了一个对象p1,我们不希望p1对象直接通过"."运算符 : p1.age 直接访问age属性,这时候我就可以设置age访问权限为private,然后通过类中的setAge()方法去访问age属性。
class People{ private int age; public void setAge(int age){ this.age=age; } } class Test{ public static void main(String args[]){ // 创建p1对象 People p1 = new People(); p1.setAge(18);// 通过setAge设置年龄。如果通过p1.age=18,是非法的。 } }
-
-
protected
顾名思义,受保护的。所以用protected关键字修饰的成员变量和方法,就称为受保护的变量和受保护的方法。
例如:下面的b就是受保护的变量,exa()就是受保护的方法。
class Pro{ protected int b; protected void exa(){ System.out.println("This is a protected method"); } }
当我们在另一个类中通过Pro类创建了一个对象pro,如果另一个类是和Pro类在同一个包中,pro可以访问protected变量和protected方法。如果不在同一个包中,就不可以访问。
-
public
顾名思义,共有的。所以用public关键字修饰的成员变量和方法,就称为共有变量和共有方法。
例如:下面的c就是共有变量变量,sum()就是共有变量方法。
class Pub{ public int c; public float sum(float a,int b){ return a+b; } }
在另一个类中通过Pub类创建了对象,对象都可以直接通过"."运算符访问共有变量和方法。对共有类变量和共有类方法来说,也可以直接通过类名访问共有类变量和共有类方法。比如:
class Example{ public static void main(String args[]){ // 创建Pub对象 Pub pub = new Pub(); float sum; // 直接通过"."运算符访问共有变量和方法 pub.c=10; sum=pub.sum(1.2f,2.5f); } }
-
友好变量和方法:
不知道大家有没有发现,上面提到了三种访问限制修饰符:public protected private,但是我们有时候见到没有访问限制修饰符的情况,之前的案例中我也大量使用了不写访问限制修饰符的方式,原因除了还没学到访问权限这一块,写的话看不懂以外,java也支持这样子写的。
-
友好变量和方法:没有访问权限修饰符修饰的变量和方法,就称为友好变量和方法。
-
例如:
class Friend{ int fri; int friendMethod(){ System.out.println("This is a friendly method called friendMethod!") } }
-
特性:对于同一包中的类,互相创建的对象可以访问自己的友好变量和方法,如果不在同一包中,而是用import导入的其他包的类,用这个类创建的对象不可以访问自己的友好变量和方法。
案例:
- 在 “类与对象.对象的组合” 包中的类Circle:
package 类与对象.对象的组合; public class Circle { double radius,area; void setRadius(double r){ radius=r; } protected double getRadius(){ return radius; } double getArea(){ area=3.14*radius*radius; return area; } }
-
在“类与对象.方法重载“包中的类Example:
package 类与对象.方法重载; //导入Circle类 import 类与对象.对象的组合.Circle; public class Example { public static void main(String[] args) { //使用Circle类 Circle circle = new Circle(); circle.radius=0;//由于radius在Circle中是友好变量,所以该操作非法,会报错。 } }
tips: 看了友好和受保护,发现他两似乎没区别对吧,都是在同一包中的类创建的对象才可以访问自己的友好变量和友好方法,不同包中不运行访问。那么他两到底有啥区别呢?区别在后面讲继承的时候会讲接区别,现在没有学继承讲了也不好理解。一步一步来吧。先留下点影响~
**访问限制修饰符按访问权限从高到低的排序:**public、protected、友好的、private。
-
-
3.13 类的划分
上面我们讲到类成员的访问权限,知道了类中的成员变量和方法划分为:public,protected、友好、private,(重复讲述加深下记忆,嘿嘿~),那么类自己是否也可以划分成不同的类呢?可以!接下来看看类的划分叭。
-
public类 :类声明的时候,在class前面加有public关键字的类。
public class Pub{ ···· }
-
友好类:在类声明的时候,没有关键字public修饰的类。
class Friend{ ··· }
类就这两种类别,没有其他划分了,可能大家觉得应该还有private和protected,但是private和protected不能修饰类哦~
-
区别:
- public类可以在其他任何一个类中创建对象。
- 友好类只能在同一个包中的类中创建对象。
3. 14 基本类型的类封装
让我们回顾下java的八种基本数据类型:boolean byte short char int long float double ,我们知道,java是一门纯面向对象的编程语言,类是基本组成。有时候我们会遇到把某一个小写字符(如:c)转化为大写字符(如:C),我们可以用下面这种方法实现:
class Example{ public static void main(String[] args){ char c='c'; //'c' 和 'C' 相差32,所以'c'的ascii码-32就得到'C'的ascii码,再强制转化为char型。 System.out.println((char)((int)c-32)); } }
我们知道在java中,程序是由类组成的,而在其他语言中,大小写转化可以用一个函数来实现,java中应该也可以用函数来实现吧,而函数在java中,都是定义在类中,换了个叫法,函数叫做方法。所以不禁疑问:java中是否有一个类,类中有一个方法,可以实现大小写转化呢?有!虽然我们可以自己写一个类来实现,但是java中,对于字符处理,他自己有一个类,存在java.lang包中,称为Character类。我们看看如何使用Character类实现大小写转换吧。
class Example{ public static void main(String[] args){ char c='c'; //使用Character类中的类方法toUpperCase方法,把c传进去,就会返回大写字母了。 System.out.println(Character.toUpperCase(c)); } }
Character类就是char这个基本数据类型的类封装,那么其他基本类型是否也有对应的封装类呢?
除了boolean 其他都有!接来下就让我们看看吧。
-
Integer :int类型的封装类。
- 构造方法:Integer(int num)
- 获取int值的方法:Integer.intValue()
-
Byte:byte类型的封装类。
- 构造方法:Byte(byte num)
- 获取byte值的方法:Byte.byteValue()
-
Short:short类型的封装类。
- 构造方法:Short(short num)
- 获取short值的方法:Short.shortValue()
-
Long:long类型的封装类。
- 构造方法:Long(long num)
- 获取long值的方法:Long.longValue()
-
Float:float类型的封装类。
- 构造方法:Float(float num)
- 获取float值的方法:Float.floatValue()
-
Double:double类型的封装类。
- 构造方法:Double(double num)
- 获取double值的方法:Double.doubleValue()
-
Character:char类型的封装类。
- 构造方法:Character(char c)
- 获取char值的方法:Character.charValue()
解析:上面仅仅只是提到了每个基本数据类型封装类的构造方法和获取值的方法,每个类中还有其他方法,可自行了解练习和以后敲代码多了自然就记住了(无需死记硬背)
3.15 对象数组
java八个基本数据类型都可以定义数组,类也是java的数据类型,是否也可以以类来定义数组,把同类的多个对象集合在一起成一个数组呢?
可以!文章来源:https://www.toymoban.com/news/detail-405486.html
- 类定义数组和基本类型定义数组是一样的形式:
class People{
public int age;
}
public class Example{
public static void main(String[] args) {
// 声明数组peo
People[] peo;
// 分配元素
peo = new People[10];
// 使用数组
for (int i = 0; i < peo.length; i++) {
People p = new People();
p.age = 18 + i;
peo[i] = p;
}
for (int i = 0; i < peo.length; i++) {
System.out.println(peo[i].age);
}
}
}
谢谢阅读~文章来源地址https://www.toymoban.com/news/detail-405486.html
到了这里,关于java类与对象(超详细!)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!