• Java面向对象详解-上


    一、类及对象

    1. 类的组成成分

    • 属性(成员变量,Field)
    • 方法(成员方法,函数,Method)

    2. 属性

    成员变量 vs 局部变量

    • 相同点:
      • 遵循变量声明的格式: 数据类型 变量名 = 初始化值
      • 都有作用域
    • 不同点:
      • 声明的位置的不同 :成员变量:声明在类里,方法外, 局部变量:声明在方法内,方法的形参部分,代码块内
      • 成员变量的修饰符有四个:public private protected 缺省,局部变量没有修饰符,与所在的方法修饰符相同
      • 初始化值:一定会有初始化值,成员变量:如果在声明的时候,不显式的赋值,那么不同数据类型会有不同的默认初始化值。 局部变量:一定要显式的赋值。(局部变量没有默认初始化值
        • byte short int long ==>0
        • float double ==>0.0
        • char ==>空格
        • boolean ==>false
        • 引用类型变量==>null
      • 二者在内存中存放的位置不同:成员变量存在于堆空间中;局部变量:栈空间中
    • 总结:
      • 按照数据类型的不同:基本数据类型(8种) & 引用数据类型
      • 按照声明的位置的不同:成员变量 & 局部变量

    3. 方法

    提供某种功能的实现

        public void eat(){//方法体}
        public String getName(){}
        public void setName(String n){}
        //格式:权限修饰符 返回值类型(void:无返回值/具体的返回值) 方法名(形参){}
    
    • 关于返回值类型
      • void:表明此方法不需要返回值
      • 有返回值的方法:在方法的最后一定有return + 返回值类型对应的变量
    • 方法内可以调用本类的其他方法或属性,但是不能在方法内再定义方法!

    4. 面向对象编程的思想的落地法则一:

    • 设计并创建类及类的成分
    • 实例化类的对象
    • 通过“对象.属性”或"对象.方法"的形式完成某项功能

    5. 类的初始化内存解析:内存划分的结构

    • 栈(stack):局部变量 、对象的引用名、数组的引用名
    • 堆(heap):new 出来的“东西”(如:对象的实体,数组的实体),含成员变量
    • 方法区:含字符串常量
    • 静态域:声明为static的变量

    6. 万事万物皆对象

    • 在Java语言范畴中,我们都能将功能、结构等封装到类中,通过类的实体化,来调用具体的功能结构
      • Scanner、String
      • 文件:File
    • 设计到Java语言与前端Html、后端的数据库交互时,都体现为类、对象

    7. 例题

    /*
     * 4. 对象数组题目:
    定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
     创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
    问题一:打印出3年级(state值为3)的学生信息。
    问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
    
    提示:
    1) 生成随机数:Math.random(),返回值类型double;  
    2) 四舍五入取整:Math.round(double d),返回值类型long。
     * 
     * 
     *  // 两位数的,随机数  10 - 99
      公式:【a,b】 :   Math.random()*(b-a+1)+a   再强转数据类型。
     * 
     */
    public class StudentTest {
    	public static void main(String[] args) {
    
    		// 声明Student类型的数组
    		Student[] stu = new Student[20];
    
    		for (int i = 0; i < stu.length; i++) {
    			// 给数组元素赋值
    			stu[i] = new Student();
    			stu[i].number = i + 1;
    			// [1,6]
    			stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
    			// [0,100]
    			stu[i].score = (int) (Math.random() * (100 - 0 + 1));
    		}
    
    		StudentTest test = new StudentTest();
    		
    		// 问题1
    		test.searchState(stu,3);
    		System.out.println("------------------------");
    		//
    		// 问题2
    		test.sort(stu);
    		test.print(stu);
    		
    	}
    
    	
    	// 遍历学生数组
    	public void print(Student[] stu) {
    		for (int i = 0; i < stu.length; i++) {
    			System.out.println(stu[i].info());
    		}
    	}
    
    	/**
    	 * 
    	 * @Description  查找指定年纪的学生
    	 * @author MD
    	 * @date 2020年7月6日下午12:02:56
    	 * @param stu 查找的数组
    	 * @param state 指定的年纪
    	 */
    	public void searchState(Student[] stu, int state) {
    		for (int i = 0; i < stu.length; i++) {
    			if (stu[i].state == 3)
    				System.out.println(stu[i].info());
    		}
    	}
    	
    	
    	public void sort(Student[] stu) {
    		for (int i = 0; i < stu.length - 1; i++) {
    			for (int j = 0; j < stu.length - i - 1; j++) {
    				if (stu[j].score <= stu[j + 1].score) {
    					// 注意,这里交换的不是成绩而是对象
    					Student temp = stu[j];
    					stu[j] = stu[j + 1];
    					stu[j + 1] = temp;
    				}
    			}
    		}
    	}
    
    }
    
    class Student {
    	int number;
    	int state;
    	int score;
    
    	public String info() {
    		return "学号:" + number + " 年级:" + state + " 分数:" + score;
    	}
    
    }
    

    二、方法的重载(overload)

    要求:
    * 同一个类中
    * 方法名必须相同
    * 方法的参数列表不同(①参数的个数不同②参数类型不同
    补充:方法的重载与方法的返回值类型没有关系!

    //如下的四个方法构成重载
    //定义两个int型变量的和
    public int getSum(int i,int j){
        return i + j;
    }
    //定义三个int型变量的和
    public int getSum(int i,int j,int k){
        return i + j + k;
    }
    //定义两个double型数据的和
    public double getSum(double d1,double d2){
        return d1 + d2;
    }
    
    //定义三个double型数组的和
    public void getSum(double d1,double d2,double d3){
        System.out.println(d1 + d2 + d3);
    }
    //不能与如上的几个方法构成重载
    //  public int getSum1(int i,int j,int k){
    //      return i + j + k;
    //  }
    //  public void getSum(int i,int j,int k){
    //      System.out.println(i + j + k);
    //  }
    
    
    //以下的两个方法构成重载。
    public void method1(int i,String str){
        
    }
    public void method1(String str1,int j){
        
    }
    

    三、可变个数的形参的方法

    • .格式:对于方法的形参: 数据类型 ... 形参名
    • 可变个数的形参的方法与同名的方法之间构成重载
    • 可变个数的形参在调用时,个数从0开始,到无穷多个都可以
    • 使用可变多个形参的方法与方法的形参使用数组是一致
    • 若方法中存在可变个数的形参,那么一定要声明在方法形参的最后
    • 在一个方法中,最多声明一个可变个数的形参
    //如下四个方法构成重载
        //在类中一旦定义了重载的可变个数的形参的方法以后,如下的两个方法可以省略
    //  public void sayHello(){
    //      System.out.println("hello world!");
    //  }
    //  public void sayHello(String str1){
    //      System.out.println("hello " + str1);
    //  }
        //可变个数的形参的方法
        public void sayHello(String ... args){
            for(int i = 0;i < args.length;i++){
                System.out.println(args[i] + "$");
            }
            //System.out.println("=====");
        }
        
        public void sayHello(int i,String ... args){
        //public void sayHello(String ... args,int i){
            System.out.println(i);
            
            for(int j = 0;j < args.length;j++){
                System.out.println(args[j] + "$");
            }
        }
        
        public void sayHello1(String[] args){
            for(int i = 0;i < args.length;i++){
                System.out.println(args[i]);
            }
        }
        
    

    四、 Java的值传递

    • 方法的参数传递(重点、难点)
      • 形参:方法声明时,方法小括号内的参数
      • 实参:调用方法时,实际传入的参数的值
    • java中的参数传递机制:值传递机制
      • 形参是基本数据类型的:将实参的值传递给形参的基本数据类型的变量
      • 形参是引用数据类型的:将实参的引用类型变量的值(对应的堆空间的对象实体的首地址值)传递给形参的引用类型变量
    • 关于变量的赋值
      • 如果变量是基本数据类型,此时赋值的是变量所保存的数据值
      • 如果变量是引用数据类型,此时赋值的变量是所保存的地址值

    1. 例一

    public static void main(String[] args) {
        TestArgsTransfer tt = new TestArgsTransfer();
        
        int i = 10;
        int j = 5;
        System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
        
    //      //交换变量i与j的值
    //      int temp = i;
    //      i = j;
    //      j = temp;
        tt.swap(i, j);//将i的值传递给m,j的值传递给n
        
        
        System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
        
    }
    //定义一个方法,交换两个变量的值
    public void swap(int m,int n){
        int temp = m;
        m = n;
        n = temp;
        System.out.println("m:" + m + " n:" + n);
    
    }
    

    2. 例二

    public class TestArgsTransfer1 {
        public static void main(String[] args) {
            TestArgsTransfer1 tt = new TestArgsTransfer1();
            DataSwap ds = new DataSwap();
            
            System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
            
            tt.swap(ds);
            System.out.println(ds);
            
            System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
            
        }
        //交换元素的值
        public void swap(DataSwap d){
            int temp = d.i;
            d.i = d.j;
            d.j = temp;
            System.out.println(d);//打印引用变量d的值
        }
    }
    
    class DataSwap{
        int i = 10;
        int j = 5;
    }
    

    3. 例3

    package com.atguigu.exer;
    
    import java.io.PrintStream;
    
    public class Test {
    
    	public static void main(String[] args) {
    		int a = 10;
    		int b = 10;
    		method(a,b);
    		System.out.println("a="+a);
    		System.out.println("b="+b);
    	}
    	
    //	public static void method(int a , int b) {
    //		a = a * 10;
    //		b = b * 20;
    //		System.out.println(a);
    //		System.out.println(b);
    //		System.exit(0);
    //	}
    	public static void method(int a, int b) {
    		PrintStream ps = new PrintStream(System.out) {
    			public void println(String x){
    				if("a=10".equals(x)) {
    					x = "a=100";
    				}else if("b=10".equals(x)) {
    					x = "b=200";
    				}
    				super.println(x);
    			}
    		};
    		
    		System.setOut(ps);
    	}
    
    	
    }
    
    

    4. 例4

    输出的什么?

    public class Test1 {
    
    	public static void main(String[] args) {
    		int[] arr = new int[] {1,2,3};
    		System.out.println(arr); //地址值
    		
    		char[] arr1 = new char[] {'a','b','c'};
    		System.out.println(arr1); //abc
    	}
    }
    
    

    五、面向对象的特征一:封装

    • 问题:当创建了类的对象以后,如果直接通过"对象.属性"的方式对相应的对象属性赋值的话,可能会出现不满足实际情况的意外,我们考虑不让对象来直接作用属性,而是通过"对象.方法"的形式,来控制对象对属性的访问。实际情况中,对属性的要求就可以通过方法来体现
    • 高内聚,低耦合
    • 面向对象思想的落地法则二:
      • 将类的属性私有化
      • 提供公共的方法(setter & getter)来实现调用
    • 四种权限修饰符
      • 权限从大到小为:public protected 缺省 private
      • 四种权限都可以用来修饰属性、方法、构造器
      • 修饰类的话:public 缺省

    • 封装性的体现
      • 将类的属性私有化,提供公共的方法来调用
      • 不对外暴露的私有化方法
      • 单例模式

    1. 构造器

    构造器的作用:①创建对象 ②给创建的对象的属性赋值

    • 设计类时,若不显式声明类的构造器的话,程序会默认提供一个空参的构造器
    • 一旦显式的定义类的构造器,那么默认的构造器就不再提供
    • 如何声明类的构造器。格式:权限修饰符 类名(形参){ }
    • 类的多个构造器之间构成重载
    • 类对象的属性赋值的先后顺序:
      • 属性的默认初始化
      • 属性的显式初始化
      • 通过构造器给属性初始化
      • 通过"对象.方法"的方式给属性赋值
    • 一个类中至少会有一个构造器,一般都提供一个空参的构造器

    2. this关键字

    • 使用在类中,可以用来修饰属性、方法、构造器
    • 表示当前对象或者是当前正在创建的对象
    • 当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
    • 在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
    • 在构造器中使用“this(形参列表)”显式的调用本类中重载的其它的构造器,要求“this(形参列表)”要声明在构造器的首行!
    public class TestPerson {
        public static void main(String[] args) {
            Person p1 = new Person();
            System.out.println(p1.getName() + ":" + p1.getAge());
            
            Person p2 = new Person("BB",23);
            int temp = p2.compare(p1);
            System.out.println(temp);
        }
    }
    class Person{
        
        private String name;
        private int age;
        
        public Person(){
            this.name = "AA";
            this.age = 1;
        }
        
        public Person(String name){
            this(); // 先调用空参数的
            this.name = name;
        }
        public Person(String name,int age){
            this(name);
            this.age = age;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public void eat(){
            System.out.println("eating");
        }
        public void sleep(){
            System.out.println("sleeping");
            this.eat();
        }
        //比较当前对象与形参的对象的age谁大。
        public int compare(Person p){
            if(this.age > p.age)
                return 1;
            else if(this.age < p.age)
                return -1;
            else
                return 0;
        }
        
    }
    

    3. package/import

    package: 声明源文件所在的包,写在程序的第一行
    import:

    • 显式导入指定包下的类或接口
    • 写在包的声明和源文件之间
    • 如果需要引入多个类或接口,那么就并列写出
    • 如果导入的类是java.lang包下的,如:System String Math等,就不需要显式的声明
    • 理解.*的概念。比如java.util. *;
    • 导入java.lang.*只能导入lang包下的所有类或接口,不能导入lang的子包下的类或接口
    • import static 表示导入指定类的static的属性或方法
    /import java.util.Scanner;
    //import java.util.Date;
    //import java.util.List;
    //import java.util.ArrayList;
    import java.lang.reflect.Field;
    import java.util.*;
    import static java.lang.System.*;
    public class TestPackageImport {
        public static void main(String[] args) {
            out.println("helloworld");
            Scanner s = new Scanner(System.in);
            s.next();
            
            Date d = new Date();
            List list = new ArrayList();
            
            java.sql.Date d1 = new java.sql.Date(522535114234L);
            
            Field f = null;
        }
    }
    
    

    六、面向对象的特征二:继承

    1. 继承的格式
      • 通过"class A extends B"类实现类的继承
    2. 子类继承父类以后,父类中声明的属性、方法,子类就可以获取到
      • 当父类中有私有的属性或方法时,子类同样可以获取得到,只是由于封装性的设计,使得子类不可以直接调用罢了
      • 子类除了通过继承,获取父类的结构之外,还可以定义自己的特有的成分
    3. java中类的继承性只支持单继承:一个类只能继承一个父类。反之,一个父类可以有多个子类
    4. 如果没有显示声明一个类的父类的话,则此类继承于java.lang.Object类

    1. 方法的重写(override orverwrite) vs 重载(overload)

    1. 重载:“两同一不同”:同一个类,同一个方法名,不同的参数列表注:方法的重载与方法的返回值无关!构造器是可以重载的
    2. 重写:(前提:在继承的基础之上,子类在获取了父类的结构以后,可以对父类中同名的方法进行“重构”)方法的返回值,方法名,形参列表形同;权限修饰符不小于父类的同名方法;子类方法的异常类型不大于父类的;两个方法要同为static或同为非static
      • 注:不能重写父类的私有的方法
      • 父类被重写的返回值类型是A类型,那么子类重写方法的返回值类型可以是A类或者A类的子类
    class Cirlce{
       //求圆的面积
        public double findArea(){
       
        } 
    
    }
    
    class Cylinder extends Circle{
          //求圆柱的表面积
          public double findArea(){
          }
    }
    

    2. 关键字 super

    1. super,相较于关键字this,可以修饰属性、方法、构造器
    2. super修饰属性、方法:在子类的方法、构造器中,通过super.属性或者super.方法的形式,显式的调用父类的指定属性或方法。尤其是,当子类与父类有同名的属性、或方法时,调用父类中的结构的话,一定要用“super.”
    3. 通过“super(形参列表)”,显式的在子类的构造器中,调用父类指定的构造器!
    4. 任何一个类(除Object类)的构造器的首行,要么显式的调用本类中重载的其它的构造器“this(形参列表)”或显式的调用父类中指定的构造器“super(形参列表)”,要么默认的调用父类空参的构造器"super()",只能二选一
    5. 建议在设计类时,提供一个空参的构造器

    3. 子类对象实例化的全过程

    无论通过那个构造器创建子类对象,需要保证先初始化父类

    目的:当子类继承父类后,继承父类中所有的属性和方法,因此子类必须知道父类如何为对象进行初始化

    public class TestDog {
        public static void main(String[] args) {
            Dog d = new Dog();
            d.setAge(10);
            d.setName("小明");
            d.setHostName("花花");
    
            System.out.println("name:" + d.getName() + " age:" + d.getAge()
                    + "hostName:" + d.getHostName());
            
            System.out.println(d.toString());
        }
    }
    
    // 生物
    class Creator {
        private int age;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public Creator() {
            super();
            System.out.println("this is Creator's constructor");
        }
    
    }
    
    // 动物类
    class Animal extends Creator {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Animal() {
            super();
            System.out.println("this is Animal's constructor");
        }
    
    }
    
    // 狗
    class Dog extends Animal {
        private String hostName;
    
        public String getHostName() {
            return hostName;
        }
    
        public void setHostName(String hostName) {
            this.hostName = hostName;
        }
    
        public Dog() {
            super();
            System.out.println("this is Dog's constructor");
        }
    }
    
    

    七、 面向对象的特征三:多态

    1. 多态性的表现:

    ①方法的重载与重写 ②子类对象的多态性

    2. 使用的前提:

    ①要有继承关系 ②要有方法的重写

    3. 格式

    • Person p = new Man();//向上转型
    • 通过父类的引用指向子类的对象实体,当调用方法时实际执行的是子类重写父类的方法

    编译时,认为p是Person类型的,故只能执行Person里才有的结构,即Man里特有的结构不能够调用,子类对象的多态性,并不使用于属性。

    调用方法:编译看左边,运行看右边

    属性:编译和运行都看左边

    package com.atguigu.java;
    
    public class AnimalTest {
    
    	public static void main(String[] args) {
    		
    		AnimalTest test = new AnimalTest();
    		// 多态性的体现
    		test.func(new Dag());
    		test.func(new Cat());
    	}
    
    	public void func(Animal an) {  // Animal an = new Dag();
    		an.eat();
    		an.shot();
    	}
    	
    	
    //	public void func(Dag dag) {
    //		dag.eat();
    //		dag.shot();
    //	}
    	
    }
    
    class Animal{
    	public void eat() {
    		System.out.println("动物:吃食物");
    	}
    	
    	public void shot() {
    		System.out.println("动物:叫");
    	}
    }
    
    class Dag extends Animal{
    	public void eat() {
    		System.out.println("狗吃肉");
    	}
    	
    	public void shot() {
    		System.out.println("汪!汪");
    	}
    }
    
    class Cat extends Animal{
    	public void eat() {
    		System.out.println("猫吃鱼");
    	}
    	
    	public void shot() {
    		System.out.println("喵!喵");
    	}
    }
    

    4. 关于向下转型

    有了对象的多态性之后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致了编译时只能调用父类中声明的属性和方法,子类中特有的属性和方法调用不了,所以有了向下转型

    • 向下转型,使用强转符:()
    • 为了保证不报ClassCastException,最好在向下转型前,进行判断: instanceof
    if (p1 instanceof Woman) {
                System.out.println("hello!");
                Woman w1 = (Woman) p1;
                w1.shopping();
      }
    
       if (p1 instanceof Man) {
            Man m1 = (Man) p1;
             m1.entertainment();
      }
    

    5. 多态是编译性行为还是运行时行为

    运行时行为

    package com.atguigu.java5;
    
    
    import java.util.Random;
    
    //面试题:多态是编译时行为还是运行时行为?
    //证明如下:
    class Animal  {
     
    	protected void eat() {
    		System.out.println("animal eat food");
    	}
    }
    
    class Cat  extends Animal  {
     
    	protected void eat() {
    		System.out.println("cat eat fish");
    	}
    }
    
    class Dog  extends Animal  {
     
    	public void eat() {
    		System.out.println("Dog eat bone");
    	}
    }
    
    class Sheep  extends Animal  {
    
    	public void eat() {
    		System.out.println("Sheep eat grass");
    	}
    }
    
    public class InterviewTest {
    
    	public static Animal  getInstance(int key) {
    		switch (key) {
    		case 0:
    			return new Cat ();
    		case 1:
    			return new Dog ();
    		default:
    			return new Sheep ();
    		}
    	}
    
    	public static void main(String[] args) {
    		int key = new Random().nextInt(3);
    
    		System.out.println(key);
    
    		Animal  animal = getInstance(key);
    		
    		animal.eat();
    	}
    }
    
    

    6. 问题1

    package com.atguigu.exer;
    /*
     * 练习:
     * 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
     * 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
     * 
     * 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
     * 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
     */
    class Base {
    	int count = 10;
    
    	public void display() {
    		System.out.println(this.count);
    	}
    }
    
    class Sub extends Base {
    	int count = 20;
    
    	public void display() {
    		System.out.println(this.count);
    	}
    }
    
    public class FieldMethodTest {
    	public static void main(String[] args) {
    		Sub s = new Sub();
    		System.out.println(s.count);//20
    		s.display();//20
    		
    		Base b = s;//多态性
    		//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
    		System.out.println(b == s);//true
    		System.out.println(b.count);//10
    		b.display();//20
    	}
    }
    
    

    7. 问题2

    package com.atguigu.exer;
    
    //考查多态的笔试题目:
    public class InterviewTest1 {
    
    	public static void main(String[] args) {
    		Base1 base = new Sub1();
    		base.add(1, 2, 3); //sub_1
    
    		Sub1 s = (Sub1)base;
    		s.add(1,2,3); //sub_2
    	}
    }
    
    class Base1 {
    	public void add(int a, int... arr) {
    		System.out.println("base1");
    	}
    }
    
    class Sub1 extends Base1 {
    
    	public void add(int a, int[] arr) {
    		System.out.println("sub_1");
    	}
    
    	public void add(int a, int b, int c) {
    		System.out.println("sub_2");
    	}
    
    }
    
  • 相关阅读:
    @RenderBody()和@RenderSection()
    C# async await 死锁问题总结
    [小技巧]你真的了解C#中的Math.Round么?
    ASP.NET MVC
    api接口返回动态的json格式?我太难了,尝试一下 linq to json
    bootstrap-table表头固定,表内容不对齐的问题
    Windows下Nginx反向代理
    Windows下Nginx的启动停止等基本操作命令详解
    Asp.NET websocket,Asp.NET MVC 使用 SignalR 实时更新前端页面数据
    Asp.NET websocket,Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室)
  • 原文地址:https://www.cnblogs.com/mengd/p/13269507.html
Copyright © 2020-2023  润新知