zoukankan      html  css  js  c++  java
  • JavaSE基础之面向对象(上)

    一、概述

    这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。

    它区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

    面向对象的语言中,包含了三大基本特征,即封装、继承和多态。

    二、类和对象

    什么是类

    类是一类具有相同特征的事物的抽象描述,是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性和行为特征来描述该类事物。
    属性:就是事物的状态信息。对比到java中可以看定为一类事物具有的参数属性。
    行为:就是事物可以做什么。对比到java中可以看定为一类事物可调用的方法。
    

    什么是对象

    对象是一类事物的具体化,是类的一个实例,具备该类事物的所有属性和行为
    比如猫
    属性:姓名、体重、种类
    行为:吃、睡、跑
    

    三、类的定义和对象的创建

    Java中用class描述事物:

    成员变量:对应事物的属性
    成员方法:对应事物的行为
    

    类的定义格式

    // 类的声明
    public class ClassName {
      //成员变量,在类中,方法外
      //成员方法 ,在类中,方法外
    }
    

    对象的创建

    new 类名()//也称为匿名对象
    
    //给创建的对象命名
    //或者说,把创建的对象用一个引用数据类型的变量保存起来
    类名 对象名 = new 类名();
    

    而对象中存储的是什么?是对象地址。

    class Student{
        
    }
    public class TestStudent{
        //Java程序的入口
        public static void main(String[] args){
            System.out.println(new Student());//Student@7852e922
    
            Student stu = new Student();
            System.out.println(stu);//Student@4e25154f
            
            int[] arr = new int[5];
    		System.out.println(arr);//[I@70dea4e
        }
    }
    //Student和TestStudent没有位置要求,谁在上面谁在下面都可以
    //但是如果TestStudent类的main中使用了Student类,那么要求编译时,这个Student已经写好了,不写是不行的
    //如果两个类都在一个.java源文件中,只能有一个类是public的
    

    可以看出,类对象和数组对象在直接打印对象名时都显示“类型@对象的hashCode值",所以说类和数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

    四、成员变量

    1、成员变量的分类

    实例变量:也叫对象属性,属于某个对象的,通过对象来使用
    类变量:也叫类变量,属于整个类的,不是属于某个实例

    2、实例变量的使用

    (1)实例变量在本类的实例方法中,直接使用

    class Circle{
        double radius;
        
        public double getArea(){
            return 3.14 * radius * radius;
        }
    }
    

    (2)实例变量在其他类的方法中,需要通过“对象名.实例变量"的方式使用,new一个实例对象

    public class TestCircle{
    	public static void main(String[] args){
    		Circle c = new Circle();
    		System.out.println("c这个圆的半径是:" + c.radius);
    		
    		//修改c这个圆的半径
    		c.radius = 1.2;
    		System.out.println("c这个圆的半径是:" + c.radius);
    	}
    }
    

    五、成员变量和局部变量的区别

    1、作用范围不一样

    成员变量:类中直接使用,其他类通过“对象名.实例变量"的方式使用
    局部变量:在当前方法中使用

    2、初始化值的不同

    成员变量:有默认值
    局部变量:无默认值,但定义时需实例化

    3、在内存中的位置不同

    成员变量:堆内存
    局部变量:栈内存

    4、生命周期不同

    成员变量:随着对象的创建或类的加载而存在,随着对象的消失而消失;没有创建对象,就不会在堆内存中分配内存地址
    局部变量: 随着方法的调用而存在,随着方法的调用完毕而消失;方法没有被调用,局部变量不会在栈中分配内存,调用一次分配一次

    六、成员方法

    1、概念

    方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。

    把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。

    2、方法的原则

    1.必须先声明后使用

    2.不调用不执行,调用一次执行一次,调用时,在栈中压入一个方法栈

    3、成员方法的分类

    实例方法:属于对象的方法,由对象来调用
    静态方法:类方法,属于整个类,不是属于某个实例,由类名来调用。

    实例方法的定义格式

    修饰符 返回值类型 方法名(参数列表:参数类型1 参数名1,参数类型2 参数名, ...... ){
            方法体;
            return 返回值;
    }
    

    实例方法的调用:在其他方法中被调用。对象名.方法名(参数);
    形参:在定义方法时方法名后面括号中的变量名称称为形式参数(简称形参),即形参出现在方法定义中。
    实参:在调用方式时,需要对方法中需要的参数进行输入,即输入实际参数参与所调用方法的执行。

    练习:声明圆类

    包含实例变量redius为圆的半径
    包含实例方法getArea求圆的面积
    包含实例方法getPerimeter求圆对象的周长
    包含实例方法getInfo获取圆对象的详细信息
    
    	public class Main{
    		public static void main(String[] agrs) {
    			Circle c = new Circle();
    			c.redius = 2.0;
    			System.out.println(c.getInfo());
    		}
    	}
    	
    	Class Circle{
    		pravite double redius;
    		
    		public double getArea() {
    			return 3.14*redius*reduis;
    		}
    		
    		public double getPerimeter() {
    			return 3.14*2*redius;
    		}
    		
    		public String getInfo() {
    			return "半径:" + radius + ",面积:" + getArea() + ",周长:" + getPerimeter();
    		}
    	}
    

    七、可变参数

    我们在定义一个方法时,如果形参的个数不确定,那么就可以使用可变参数

    八、方法重载

    方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
    参数列表:数据类型个数不同,数据类型不同,数据类型顺序不同。
    重载方法调用:JVM通过方法的参数列表,调用不同的方法。

    我们看一下下段代码:

    class Test06_Overload_Problem2{
    	public static void main(String[] args){
    		Count c = new Count();
    		System.out.println(c.sum(1,2));//(int a, int b)
    		System.out.println(c.sum(1,2,3));//(int... args)和(int a, int... args)都兼容,就有问题了
    	}
    }
    //Count类编译没问题,但是调用者有问题
    class Count{
    	public int sum(int a, int b){
    		return a+b;
    	}
    	public int sum(int... args){
    		int sum = 0;
    		for(int i=0; i<args.length; i++){
    			sum += args[i];
    		}
    		return sum;
    	}
    	public int sum(int a, int... args){
    		int sum = a;
    		for(int i=0; i<args.length; i++){
    			sum += args[i];
    		}
    		return sum;
    	}	
    }
    

    九、方法的参数传递机制

    方法的形参是基本数据类型时,形参值的改变不会影响实参;
    方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。

    机制一、

    public class ParameterTransfer {
         public static void main(String[] args) {
            int num = 30;
            System.out.println("调用add方法前num=" + num); // 30
            add(num);
            System.out.println("调用add方法后num=" + num); // 30
         }
      
         public static void add(int param) {
              param = 100;
         }
    }      
    

    我们可以从内存模型来进行分析:
    当执行了int num = 30;语句后,JVM在栈内存中开辟一块地址为0X111的内存,里面存放的值为30;当执行add(num);后,JVM又在栈内存中开辟一块地址为0X222的内存,先存放的是30,执行完param = 100;后,0X222的值变成100。但无论怎么改变,0X222中的param值与0X111中的num值没有关系,所以怒骂值不会改变。

    机制二、

    public class ParameterTransfer {
         public static void main(String[] args) {
              String[] array = new String[] {"huixin"};
              System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
              reset(array);
              System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
         }
      
         public static void reset(String[] param) {
              param[0] = "hello, world!";
         }
    }
    

    当传递的是引用数据类型时,结果就不一样了。

    当程序执行了String[] array = new String[] {"huixin"}后,JVM在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。

    当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址。

    而在方法reset中修改了param[0]的值为"hello, 改变了world!"时,实际上则是改变了param AD9600指向堆内存中的地址BE2500内的值。而array AD9500同样指向堆内存BE2500,因此会发生改变。

    而同样这个问题也可以看作为对象类型的变量。即String 为new出的对象;但当String为直接赋值而不是new时,则可以看作是基本数据类型。然而String类型是不可变,即创建就不可修改。

    https://blog.51cto.com/freej/168676

    十、对象数组

    数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。当元素是引用数据类型是,我们称为对象数组。

    我们首先要创建对象数组,并且确定数组的长度。否则容易出现空指针异常。

    我们通过代码来分析:

    Class Preson {
    	private String name;
    	private int age;
    	private double length;
    }
    
    public class Main{
    	public static void main(String[] args) {
    		Preson[] preson = new Preson[4];
    		
    		// 数组不需要初始化,因为数组会自动初始化为null
    		for(int i = 0; i < preson.length; i++) {
    			preson[i] = new Preson(); // 定义了属性类型为Preson,里面的元素只能存放Preson对象或子类对象
    			preson[i].name = "name" + i;
    			preson[i].age = i;
    			preson[i].length = 100.0 +i;
    		}
    	}
    } 
    

    我们来看一下代码的内存模型

  • 相关阅读:
    Python记录12:迭代器+生成器+生成式
    Python记录11:叠加多个装饰器+有参装饰器
    Python记录10:模块
    Day7
    Day7
    Day7
    Day7
    Day7
    Day7
    Day7
  • 原文地址:https://www.cnblogs.com/njuptzheng/p/13232930.html
Copyright © 2011-2022 走看看