面向对象
面向对象&面向过程
- 就日常编程而言,在未接触完整项目和复杂案例之前几乎都是面向过程。
- 顾名思义面向过程强调的是过程,按照顺序一步一步的进行
我上厕所:
打开厕所门---->关上厕所门--->脱裤子---->开始用劲--->擦完站起来--->打开厕所门走人
- 面向对象则重点在对象:万物皆对象这句话非常正确。任何一个对象都有自身的特征属性、行为动作等
- 两者的主要区别其实就在于关注的核心不同
- 将上述举例换成面向对象的思想:
我上厕所:
对象:我、厕所
行为:厕所开关门、我脱裤子、我用劲
- 对于完整项目和复杂案例而言,需要在系统的整体设计上采用面向对象的思想,但具体到某一微观操作仍需要利用面向过程的思想去处理
- Java语言是一种面向对象的程序设计语言,而面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。它区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。
面向对象三大特性
-
封装:
- 封装作为面向对象的核心特性,拥有两层含义:
- 将对象的属性和行为看作整体
- 将不需要让外界(用户)看到的信息隐藏起来;
- 封装作为面向对象的核心特性,拥有两层含义:
-
继承
- 继承性主要描述类与类之间的关系。通过继承,可以在无需重新编写原有类的情况下,对原有类机型扩展
-
多态
- 在一个类中定义的属性和方法被其他类继承后,可以具有不同的数据类型,表现出不同的行为动作,同一个属性在不同类中具有不同的意义
类与对象的创建
首先来理解类和对象
- 类:可以看作是一部分具有共同特征属性的集合。比如“学生”,就可以看作是一个类。
- 而通常类会包含两部分内容:
- 成员变量(可以理解为一部分对象的共同特征,如“学生”的姓名、学号、性别等)
- 成员方法(理解为这些对象所都具有的行为动作,如“学生”的上课、写作业等)
- 对象:在一个类中的具体实例。“学生”中的小红、小明就可以被称为对象。
类是抽象的,但是对象是真是存在的
对象是面向对象思想的核心,但在创建对象之前首先要创建类
- 创建类:
public class 类名{//也可以省略public
成员变量;
成员方法;
}
//下面定义一个学生类
class student{
//成员变量 姓名年龄
String neme;
int age;
//成员方法 自我介绍
void Hello(){
System.out.println("Hello,我是"+name+“我今年”+age+"岁啦");
}
}
- 创建对象:
类名 对象名 = new 类名();
- 创建并实例化对象
- 注意:在一个项目中通常只存在一个main方法
//玩具类
public class toys {
//属性:字段
String name;
int size;
//方法
public void photo() {
}
}
public class market {
public static void main(String[] args) {
//实例化之后返回一个具体对象
//car和ball就是玩具类的一个具体实例
toys car = new toys();
toys ball = new toys();
/*
使用对象访问类中的某法方法或者属性
对象名.属性名
对象名.方法名
实例化对象之后可以对属性进行赋值
*/
car.name = "大黄蜂";
car.size = 17;
ball.name = "篮球";
ball.size = 5;
System.out.println(car.name+"是個黃色的汽車人");
System.out.println(ball.name+"是個籃球");
}
}
创建对象
如何创建对象
-
匿名对象
new 类名()//也称为匿名对象 public class PersonMainTest{ public static void main(String[]args){ //匿名创建对象(此对象,仅仅使用一次) new Person().name="张三"; new Person().age=30; } }
-
给创建的对象命名
//给创建的对象命名 //或者说,把创建的对象用一个引用数据类型的变量保存起来 类名 对象名 = new 类名();
-
用法
// 使用Peron类定义一个Person类型的变量 Person p; // 通过new关键字调用Person类的构造器,返回一个Person实例, // 将该Person实例赋给p变量。 p = new Person(); // 访问p的name实例变量,直接为该变量赋值。 p.name = "李刚"; // 调用p的say方法,声明say()方法时定义了一个形参, // 调用该方法必须为形参指定一个值 p.say("Java语言很简单,学习很容易!"); // 直接输出p的name实例变量,将输出 李刚 System.out.println(p.name);
对象名中存储的是什么
// 使用Peron类定义一个Person类型的变量
Person p;
// 通过new关键字调用Person类的构造器,返回一个Person实例,
// 将该Person实例赋给p变量。
p = new Person();
// 访问p的name实例变量,直接为该变量赋值。
p.name = "李刚";
// 调用p的say方法,声明say()方法时定义了一个形参,
// 调用该方法必须为形参指定一个值
p.say("Java语言很简单,学习很容易!");
// 直接打印对象名
System.out.println(p);
- 直接打印对象名和数组名都是显示“类型@对象的hashCode值",那么像“Person@4e25154f”是对象的地址吗?不是,因为Java是对程序员隐藏内存地址的,不暴露内存地址信息,所以打印对象时不直接显示内存地址,而是JVM提取了对象描述信息给你现在,默认提取的是对象的运行时类型@代表对象唯一编码的hashCode值。
成员变量
成员变量的分类
-
实例变量:没有static修饰,也叫对象属性,属于某个对象的,通过对象来使用。
-
类变量:有static修饰,也叫类变量,属于整个类的,不是属于某个实例。
如何声明成员变量?
【修饰符】 class 类名{
【修饰符】 数据类型 属性名; //属性有默认值
【修饰符】 数据类型 属性名 = 值; //属性有初始值
}
说明:属性的类型可以是Java的任意类型,包括基本数据类型、引用数据类型(**类、**接口、数组等)
例如:声明一个中国人的类
class Chinese{
static String country;
String name;
char gender = '男';//显式赋值
}
成员变量内存
如何在类外面访问成员变量?
(1)类变量
类名.静态成员变量 //推荐
对象名.静态成员变量 //不推荐
(2)实例变量
对象名.静态成员变量 //只能使用这种方式
例如:
public class TestChinese {
public static void main(String[] args) {
//类名.静态成员变量
System.out.println(Chinese.country);
//错误,非静态成员变量必须通过对象.进行访问
// System.out.println(Chinese.name);
Chinese c1 = new Chinese();
//对象名.非静态成员变量
System.out.println(c1.name);
//静态的成员变量也可以通过对象.进行访问
//对象名.非静态成员变量
System.out.println(c1.country);
System.out.println(c1.gender);
}
}
class Chinese{
static String country;
String name;
char gender = '男';
}
成员变量的特点
(1)成员变量有默认值
基本类型 | 整数(byte,short,int,long) | 0 |
---|---|---|
浮点数(float,double) | 0.0 | |
字符(char) | ‘\u0000’ | |
布尔(boolean) | false | |
数据类型 | 默认值 | |
引用类型 | 数组,类,接口 | null |
(2)类变量的值是所有对象共享的,而实例变量的值是每个对象独立的
public class TestChinese {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
c1.name = "张三";
c2.name = "李四";
c2.gender = '女';
// c1.country = "中国";
Chinese.country = "中国";//推荐
System.out.println("c1.country = " + c1.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
System.out.println("c2.country = " + c2.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
}
}
class Chinese{
static String country;
String name;
char gender = '男';
}
成员方法
概述
- 成员变量是用来存储对象的数据信息的,那么如何表示对象的行为功能呢?就要通过方法来实现,方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。
方法的原则
方法的使用原则:
(1)必须先声明后使用
类,变量,方法等都要先声明后使用
(2)不调用不执行,调用一次执行一次。
成员方法的分类
成员方法分为两类:
- 实例方法:没有static修饰的方法,必须通过实例对象来调用。
- 静态方法:有static修饰的方法,也叫类方法,可以由类名来调用。
如何声明方法
1、方法声明的位置必须在类中方法外
2、语法格式
【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ...... 】){
方法体;
【return 返回值;】
}
-
修饰符: 修饰符后面一一介绍,例如:public,static等都是修饰符
-
返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
- 基本数据类型
- 引用数据类型
- 无返回值类型:void
-
方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
-
参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
-
方法体:特定功能代码
-
return:结束方法,并将方法的结果返回去,
- 如果返回值类型不是void,方法体中必须保证一定有return 返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
- 如果返回值类型为void时,return 后面不用跟返回值,甚至也可以没有return语句。
- return语句后面就不能再写其他代码了,否则会报错:Unreachable code
-
举例
声明一个圆的图形类:
属性(成员变量):半径,
成员方法:求面积的方法,返回圆对象信息的方法
在测试类的main中,创建圆的2个对象,为半径属性赋值,调用两个方法进行测试
提示:圆周率为Math.PIclass Circle{ double radius; double area() { return Math.PI * radius * radius; } }
如何在其他类中调用方法
(1)实例方法
对象名.实例方法(【实参列表】) //必须通过对象来访问
代码举例:
public class TestCircle {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 1.2;
System.out.println("c1的面积:" + c1.area());
//非静态方法只能通过"对象."进行访问
//System.out.println("c1的面积:" + Circle.area());
Circle c2 = new Circle();
c2.radius = 2.5;
System.out.println("c2的面积:" + c2.area());
}
}
class Circle{
double radius;
public double area() {
return Math.PI * radius * radius;
}
}
(2)类方法
类名.类方法(【实参列表】) //推荐
对象名.类方法(【实参列表】) //不推荐
示例:
public class TestCount {
public static void main(String[] args) {
System.out.println(CountTools.max(4, 1));
//静态方法也可以通过“对象.”访问,就是麻烦点
CountTools c = new CountTools();
System.out.println(c.max(2, 5));
}
}
class CountTools{
static int max(int a, int b) {
return a > b ? a : b;
}
}
(3)形参、实参
- 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
- 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
- 总结:
(1)调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应
如果方法没有形参,就不需要也不能传实参。
(2)调用时,如果方法有返回值,可以接受或处理返回值结果,当然也可以不接收,那么此时返回值就丢失了。
如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。
在本类中访问本类的成员变量和成员方法
-
直接用,不需要加“对象名.“和"类名.”
-
唯一例外:静态方法中不能直接访问本类的非静态的成员变量和成员方法
class Test{ static void test(){ System.out.println(""); } void method(){ test(); } public static void main(String[] args){ method();//错误 test();//正确 } }
方法调用内存分析
-
方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。栈结构:先进后出,后进先出。
-
实例一:
public class TestCount { public static void main(String[] args) { int a = 4; int b = 2; int m = CountTools.max(a, b)); } } class CountTools{ static int max(int a, int b) { return a > b ? a : b; } }
public class TestCircle { public static void main(String[] args) { Circle c1 = new Circle(); c1.radius = 1.2; int area1 = c1.area(); Circle c2 = new Circle(); c2.radius = 2.5; int area2 = c2.area(); } } class Circle{ double radius; public double area() { return Math.PI * radius * radius; } }
2.8.8 方法的参数传递机制
- 方法的参数传递机制:实参给形参赋值
- 方法的形参是基本数据类型时,形参值的改变不会影响实参;
- 方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
- 注意:String、Integer等特殊类型容易错
示例代码1:
class Test{
public static void swap(int a, int b){
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args){
int x = 1;
int y = 2;
swap(x,y);//调用完之后,x与y的值不变
}
}
示例代码2:
class Test{
public static void change(MyData my){
my.num *= 2;
}
public static void main(String[] args){
MyData m = new MyData();
m.num = 1;
change(m);//调用完之后,m对象的num属性值就变为2
}
}
class MyData{
int num;
}
示例代码3:
public class Test {
public static void main(String[] args) {
int[] arr = {2,4,1,5,3};
ArrayUtil.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
class ArrayUtil{
public static void sort(int[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
陷阱1:
/*
陷阱1:在方法中,形参 = 新new对象,那么就和实参无关了
*/
class Test{
public static void change(MyData my){
my = new MyData();//形参指向了新对象
my.num *= 2;
}
public static void main(String[] args){
MyData m = new MyData();
m.num = 1;
change(m);//调用完之后,m对象的num属性值仍然为1
}
}
class MyData{
int num;
}
陷阱2:见字符串和包装类部分
public class Test {
public static void main(String[] args) {
StringUtil util = new StringUtil();
String str = "我不好";
util.change(str);
System.out.println(str);
}
}
class StringUtil{
public void change(String str){
str += "你好";//String对象不可变,一旦修改就会产生新对象
}
}
构造器
- 在上文“创建并实例化对象”中,提到在对象实例化之后可以通过 “ 对象名.属性名 = 值; ”进行赋值。但如果要在对象实例化的同时对该对象的属性进行赋值就要用到构造器。
- 构造器也成为构造方法,属于类的一个特殊成员方法,在类实例化对象是会自动调用。
- 构造器定义:
- 构造器名称需与类名称一致
- 构造器名称前不能有需要返回值的数据声明
- 构造器中没有返回值
class student{
public student(){
//这是一个无参构造器
}
}
- 无参构造器是是默认存在的(一般为隐式)
- 含参构造器:
public class play {
String name;
//含参构造
public play(String name) {
this.name = name;
}
}
-
需要注意:一旦定义了含参构造器,若要使用无参构造则必须显示定义无参构造
-
无参构造器调用:
public class ct {
public ct() {
System.out.println("调用了此构造器");
//无参构造器
}
}
public class tt {
public static void main(String[] args) {
ct cc = new ct();
}
}
- 含参构造器定义及调用:
public class ct {
String name;
public ct(String name) {
//含参构造器
this.name = name;
//第一个name与ct类中定义name相同
//等号后name为被调用时接受数据的name
}
void prin(){//打印
System.out.println("我是"+name);
}
}
public class tt {
public static void main(String[] args) {
ct ct1 = new ct("张三");//实例化同时直接赋值
ct1.prin();
}
}