zoukankan      html  css  js  c++  java
  • Java基础06—类和对象

    类和对象


    参考资料:《Java从入门到精通》/明日科技编著. 4版. 北京:清华大学出版社,2016

    一、面向对象概述

    面向对象是一种开发思想,它将所有预处理的问题抽象成对象,同时了解这些对象具有哪些相应的属性和行为,以解决这些对象面临的实际问题。

    1、对象

    • 对象是指事实存在的实体,如桌子、电脑、手机等;
    • 通常将对象划分为两个部分,即静态部分动态部分
    • 静态部分被称为“属性”,动态部分称为“行为”。
    • 在Java语言中,对象的属性是以成员变量的形式定义的,对象的行为是以方法的形式定义的;
    • 在Java语言中,对象就是符合某个类的定义所产生出来的实体

    2、类

    • 类是具有相同特性和行为的一类事物;
    • 类是描述某一类事物的统称;
    • 类是封装对象的属性和行为的载体,例如鸟类封装了所有鸟的共同属性和应有的行为。

    3、封装

    面向对象程序设计有3个特点:

    • 封装性
    • 继承性
    • 多态性

    封装的思想就是将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现的细节。采用封装思想保证了类内部数据结构的完整性,应用该类的用户不能轻易地直接操作此数据结构,避免了外部操作对内部数据地影响,提高了程序地可维护性。

    4、继承

    类与类之间同样具有关系,类之间地这种关系叫做关联,如教师类与学生类就是一个关联。两个类之间的关系有多种,继承只是关联的一种。

    在处理一个问题时,可以将一些有用的类保存下来,遇到相同问题时拿来复用。比如处理信鸽送信问题时,创建信鸽类时可以将鸟类拿来复用,保留鸟类的属性和方法,再添加一些信鸽具有的独特属性和行为。这样就节省了定义鸟类和信鸽类的共同属性和行为的时间,这就是继承的基本思想。

    在Java语言中,将类似于信鸽类的类称为子类,将类似于鸟类的类称为父类超类。值得注意的是,可以说信鸽是鸟,但不能说鸟是信鸽,也就是说子类的实例都是父类的实例,但不能说父类的实例是子类的实例,

    在继承体系中,一个类既可以是其他类的父类,为其他类提供属性和行为,也可以是其他类的子类,继承父类的属性和方法。比如老虎是猫科类的子类,也是东北虎的父类。

    5、多态

    • 其实将父类对象应用于子类的特征就是多态;
    • 多态的实现并不依赖具体类,而是依赖于抽象类和接口;
    • 抽象类并不能实例化对象。
    • 在多态机制中,父类通常被定义为抽象类,在抽象类中给出一个方法的标准,而不能给出具体的实现流程,实际上这个方法也是抽象的,并没有给出具体的流程。
    • 在多态的机制中,比抽象类更方便的方法是将抽象类定义为接口,由抽象方法组成的集合就是接口。

    二、类

    1、成员变量

    在Java中,对象的属性也称为成员变量。下面通过代码的形式来了解成员变量,以及在类中成员变量所处的位置。

    public class Book {
        //定义String类型的成员变量
        private String name;
        //定义一个getName()方法
        public String getName(){
            int d = 0;   //局部变量
            setName("Java");   //调用类中的其他方法
            return id + this.name;
        }
        ////定义一个setName()方法
        public void setName(String name){
            //将参数值赋予类中的成员变量
            this.name = name;
        }
        //返回Book类引用
        public Book getBook(){
            return this;
        }
    }
    

    2、成员方法

    在Java中,成员方法对应于类对象的行为。定义成员方法的语法格式如下:

    权限修饰符  返回值类型  方法名(参数类型  参数名){
        方法体
        return 返回值;
    }
    
    • 成员方法可以没有参数,也可以有参数,这个参数可以是对象,也可以是基本数据类型的变量;
    • 成员方法有返回值和不返回任何值的选择,如果需要返回值,在方法体中加入return关键字;
    • 成员方法无任何返回值,可以使用void关键字表示;
    • 在成员方法中可以调用其他成员方法和类成员变量。
    • 类成员变量和类方法可以统称为类成员

    3、权限修饰符

    Java中的权限修饰符包括public、private和protected,这些修饰符控制着对类和类成员变量以及成员方法的访问。

    • 被修饰为privat的类成员变量成员方法,则该成员变量或成员方法只能在本类中被使用,在子类中是不可见的,并且对其他类也是不可见的。
    • 被修饰为public的类成员变量成员方法,则该成员变量或成员方法除了在本类可以使用外,还可以在其他子类和其他包的类中使用。
    • 如果一个的访问权限被设置成了private,那么这个类将隐藏其中所有的数据,以免用户直接访问它。
    • 如果一个的访问权限被设置成了protected,那么只有本包内的类可以访问此类中的成员变量和成员方法。

    注意:当声明类时没有使用public、private和protected修饰符,则这个类预设为protected权限,即只有一个包内的类可以调用此类的成员变量和成员方法。

    //在com.xuliang包下创建Xu类
    package com.xuliang;
    
    class Xu {
        public String getName(){
            int id = 0;  
            return id;
        }
    }
    
    

    上述代码中,由于类的修饰符为默认修饰符,即只有一个包内的其他类和子类可以访问该类,可以而getName()方法却又被设置成了public权限,即使这样,getName()方法的访问权限依然与Xu类的权限相同。

    注意:在Java语言中,类的权限设定会约束成员的权限设定。

    4、局部变量

    在成员方法内定义一个变量,那么这个变量被称为局部变量。

        public String getName(){
            int id = 0;   //局部变量
            setName("Java");   //调用类中的其他方法
            return id + this.name;
        }
        
        public void setName(String name){
            //将参数值赋予类中的成员变量
            this.name = name;
        }
    

    上述代码中,getName()方法的id变量即为局部变量。实际上方法中的形参也可作为一个局部变量,如setName(String name)方法中的String name就被看作一个局部变量。

    • 局部变量在方法被执行时创建,在方法执行结束时被销毁;
    • 局部变量在使用时必须进行赋值操作或者初始化,否则会出现编译错误。

    5、局部变量的有效范围

    局部变量的有效范围称为变量的作用域。

    public String getName(){
        int id = 0;   //局部变量
        for (int i = 0; i < 10; i++) {   
            System.out.println(i);       
        }
    
    • 局部变量id的作用域为getName()方法中;
    • 局部变量i的作用域为for循环中;
    • 在互不嵌套的作用域中,可以同时声明两个名称和类型相同的局部变量。
    public String getName(){
        int id = 0;   //局部变量
        for (int i = 0; i < 10; i++) {   //定义局部变量i
            System.out.println(i);        
        }
        for (int i = 0; i < 10; i++) {   //定义局部变量i
            System.out.println(i);       
        }
    

    6、this关键字

        public void setName(String name){
            //this.name代表此类的name成员变量
            this.name = name;    //将参数值赋予类中的成员变量
        }
    

    在Java语言中,规定了使用this关键字来代表本类对象的引用,this关键字可以调用成员变量和成员方法。事实上,this引用的就是本类的一个对象,当局部变量与成员变量重名时,如上述代码的情况,就要引入this关键字来明确引用的是类成员还是局部变量。

    this关键字除了可以调用成员变量和成员方法外,还可以作为成员方法的返回值。

        public Xu getBook(){
            return this;  //返回Xu类引用
        }
    

    在上述代码中,getBook()方法的返回值类型为Xu类,所以方法体中使用return this这种形式将Xu类的对象进行返回。

    7、类的构造方法

    • 在一个类中,除了成员方法外,还存在一种特殊的方法,那就是构造方法
    • 构造方法是一个与类同名的的方法,对象的创建就是通过构造方法完成的。
    • 每当类实例化一个对象时,类就会自动调用构造方法。

    构造方法的特点:

    1. 构造方法没有返回值;
    2. 构造方法的名称与本类的名称相同。

    注意:构造方法没有返回值,但构造方法并不需要使用void关键字进行修饰。

    public class Book{
        public Book(){
            ...//构造方法体
        }
    }
    
    • public:构造方法的修饰符。
    • Book:构造方法的名称。
    1. 在构造方法体中可以为成员变量赋值,这样当实例化一个本类的对象时,相应的成员变量也将被初始化。
    2. 如果类中没有明确定义构造方法,编译器就会自动创建一个不带参数的默认构造方法。

    注意:只有在类中没有定义任何构造方法时,编译器才会在该类中自动创建一个不带参数的构造方法。

    事实上,this关键字还可以调用类中的构造方法,如下所示:

    public class Book{
        public Book(){                    //定义无参构造函数
            this("this调用有参构造函数")   //使用this调用有参构造函数
            System.out.println("这是无参构造函数");
        }
        
        public Book(String name){          //定义有参构造函数
            System.out.println("这是有参构造函数");
        }
    }
    

    注意:只可以在无参构造方法中的第一句使用this调用有参构造函数。

    三、静态变量、常量和方法

    先介绍static关键字,因为由static修饰的变量、常量和方法被称为静态变量、静态常量和静态方法。

    在处理问题时,可能会遇到这样的情况:在球类中需要用到PI这个常量,可能圆类也需要使用这个常量,这时没有必要在两个类中同时创建PI常量,因为这样系统会将不同类中定义的常量分配到不同的内存空间。为了解决这个问题,可以将PI常量设为静态的。

    被声明为static的变量、常量和方法统称为静态成员。静态成员属于类所有,区别于个别对象,可以在本类或者其他类中使用“类名.静态成员”的方式调用该类的静态成员。

    类名.静态成员
    
    package com.xuliang;
    
    public class StaticTest {
        //定义静态常量
        final static double PI = 3.1415;
        //定义静态变量
        static int id;
        //定义静态方法
        public static void method(){
            System.out.println("hello");
        }
        
        public void test(){
            //调用静态常量
            System.out.println(StaticTest.PI);
            //调用静态变量
            System.out.println(StaticTest.id);
            //调用静态方法
            StaticTest.method();
        }
    }
    

    注意:虽然静态成员也可以使用“对象.静态成员”的方式调用,但通常不建议这样的形式,因为这样容易混淆静态成员和非静态成员。

    静态数据和静态方法的作用通常是为了提供共享数据或方法,如数学计算公式等。尽管使用这种方式调用静态成员比较方便,但静态成员同样遵循着public、private和protected修饰符的约束。

    package com.xuliang;
    
    public class StaticTest {
        //定义静态常量
        final static double PI = 3.1415;
        //定义静态变量
        static int id;
        //定义静态方法
        public static void method(){
            System.out.println("hello");
        }
        
        //定义非静态方法
        public void test(){
            //调用静态常量
            System.out.println(StaticTest.PI);
            //调用静态变量
            System.out.println(StaticTest.id);
            //调用静态方法
            StaticTest.method();
        }
        
       //定义静态方法 
        public static StaticTest method2(){
            method();      //调用非静态方法(错误代码)
            return this;   //在return中使用this关键字(错误代码)
        }
    }
    

    输入上述代码后,编译器会发生错误,这是因为method2()方法是一个静态方法,而在其方法体中调用了非静态方法和this关键字。

    在Java语言中,对静态方法有两点规定:

    1. 在静态方法中不可以使用this关键字;
    2. 在静态方法中不可以调用非静态方法。

    注意:Java语言规定,不能将方法体中的局部变量声明为static。

    技巧:如果在执行类时,希望先执行类的初始化动作,可以使用static定义一个静态区域,如下所示:

    public class example{
        static{
            //执行序列
        }
    }
    //当这段代码被执行时,首先执行static块中的代码
    

    通过下面的例子来了解static{}代码块的作用:

    public class HelloWorld {
        static {
            System.out.println("hello world!");
        }
    
        public static void main(String[] args) {
            System.exit(0);
        }
     } 
     
     输出结果:
     hello world!
    

    在这个类中,主方法的第一句就是让程序正常结束,但是程序依然输出了“hello world!”。这是因为static语句会在main()方法调用之前执行,静态块的特点就是在类加载的时候执行,且只执行一次。

    除了static{}代码块,还有匿名代码块,代码的执行顺序如下所示:

    public class HelloWorld {
    
        //静态代码块
        static {
            System.out.println("静态代码块");
        }
    
        //匿名代码块
        {
            System.out.println("匿名代码块");
        }
    
        //构造方法
        public HelloWorld(){
            System.out.println("构造方法");
        }
    
        public static void main(String[] args) {
            HelloWorld helloWorld = new HelloWorld();
        }
    }
    
     输出结果:
    静态代码块
    匿名代码块
    构造方法
    

    由上述执行结果可知,最先执行静态代码块,其次是匿名代码块,最后是构造方法。

    四、类的主方法

    主方法是类的入口点,它定义了程序从何处开始。主方法提供了对程序流向的控制,java编译器通过主方法来执行程序。主方法的格式如下所示:

    public static void main(String[] args) {
        //方法体
        StaticTest staticTest = new StaticTest();
        staticTest.test();    //test()不是静态方法,必须先实例化
        
        method();    //静态方法则可以直接调用
    }
    

    主方法具有以下特性:

    • 主方法是静态的,所以如果要直接在主方法中调用其他方法,则该方法必须也是静态的。
    • 主方法没有返回值。
    • 主方法的方法名必须为“main”。
    • 主方法的形参必须为String类型数组,其中args[0]~args[n]分别代表程序的第一个参数到第n个参数,可以使用args[].length获取参数的个数。
        public static void main(String[] args) {
            for (int i = 0; i < args.length; i++) {
                System.out.println(args[i]);
            }
    
    • String[] args数组的作用是接收命令行输入的参数,命令行的参数之间用空格隔开。
    D:Studyasetestsrc>javac TestMain.java 
    D:Studyasetestsrc>java TestMain 1 2 3
    //则args[0]=1、args[1]=2、args[2]=3
    

    注意:主方法的形式是固定的,以下形式都不能作为程序入口:

    public static void main()          //必须带String[] args参数
    public void main(String[] args)    //必须修饰为static
    public static main(String[] args)  //返回值必须为void
    

    五、对象

    Java是一门面向对象的编程语言,对象是由类抽象出来的,所有的问题都是通过对象来处理,对象可以操作类的属性和方法来相应的问题,所以了解对象的产生、操作和消亡是十分重要的。

    1、对象的创建

    • 对象可以认为是在一类事物中抽象出某一特例,可以通过这个特例来处理这类事物出现的问题。
    • 在Java语言中,通过new关键字来创建对象。
    • 创建对象的过程,实质上是调用一次构造方法的过程。准确来说,使用new关键字调用构造方法来创建对象。
    Test test = new Test();
    Test test = new Test("abc");   //调用有参构造函数
    

    其参数说明如下所示:

    • Test:类名
    • test:对象名
    • new:创建对象的操作符
    • "abc":构造方法的参数

    test对象被创建出来后,就是一个对象的引用,这个引用在内存中为对象分配了存储空间。之前介绍过,可以在构造方法中初始化成员变量,当创建对象时,自动调用构造方法。也就是说,在Java语言中初始化和创建是被捆绑在一起的。

    每个对象都是相对独立的,在内存中占据独立的内存地址,并且每个对象都具有自己的生命周期。当一个对象的生命周期结束时,对象就会变成垃圾,由Java虚拟机自带的垃圾回收机制处理,不能再被使用。

    注意:在Java语言中,对象和实例实际上可以通用。

    public class CreateObject {
        //构造方法
        public CreateObject(){
            System.out.println("这是构造方法");
        }
    
        public static void main(String[] args) {
            new CreateObject();    //创建对象
            CreateObject createObject = new CreateObject();   //创建对象
        }
    }
    
    输出结果:
    这是构造方法
    这是构造方法
    

    上述代码中,在主方法中使用new操作符创建对象,创建对象的同时,将自动调用构造方法中的代码。

    2、访问对象的属性和行为

    使用new创建一个对象后,就可以使用“对象.类成员”来获取对象的属性和行为,即获取对象的成员变量和成员方法。

    public class Transfer {
        //成员变量
        int id = 12;
        //成员方法
        public void call(){
            for (int i = 0; i < 3; i++) {
                System.out.print(i + " ");
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            //创建对象
            Transfer transfer1 = new Transfer();
            Transfer transfer2 = new Transfer();
            
            //对象transfer1调用成员变量,并重新赋值
            transfer1.id = 24;
            System.out.println("transfer1对象调用成员变量id的结果:" + transfer1.id);
            //对象transfer1调用成员方法
            transfer1.call();
            
            //对象transfer2调用成员变量
            System.out.println("transfer2对象调用成员变量id的结果:" + transfer2.id);
            //对象transfer2调用成员方法
            transfer2.call();
        }
    }
    
    输出结果为:
    transfer1对象调用成员变量id的结果:24
    0 1 2 
    transfer2对象调用成员变量id的结果:12
    0 1 2 
    

    由上述代码的执行结果可知,两个对象创建后是相对独立的,改变了transfer1对象的id值,并不会影响transfer2对象的id值。

    public class Transfer {
        //定义静态成员变量
        static int id = 12;
        public void call(){
            for (int i = 0; i < 3; i++) {
                System.out.print(i + " ");
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            Transfer transfer1 = new Transfer();
            Transfer transfer2 = new Transfer();
    
            transfer1.id = 24;
            System.out.println("transfer1对象调用成员变量id的结果:" + transfer1.id);
            transfer1.call();
    
            System.out.println("transfer2对象调用成员变量id的结果:" + transfer2.id);
            transfer2.call();
        }
    }
    
    输出结果为:
    transfer1对象调用成员变量id的结果:24
    0 1 2 
    transfer2对象调用成员变量id的结果:24
    0 1 2 
    

    由上述代码执行结果可知,使用static关键字使得成员变量id的值可以被本类或其他类共享。两个对象在调用一个同一个静态变量时,都是指向同一块内存区域。

    3、对象的引用

    在Java语言中,尽管一切都可以看作是对象,但真正的操作标识符实质上是一个引用,语法如下所示:

    类名  对象引用名称
    
    Book  book;      //创建对象引用
    book = new Book();    //创建一个对象,并与对象引用book相关联
    

    对象引用不一定需要有一个对象相关联。

    注意:引用只是存放一个对象的内存地址,并非存放一个对象。严格来说,引用和对象是不同的,但是通常情况下可以将这种区别忽略。可以简单地说book是Book类的一个对象,而事实上应该是book包含Book对象的一个引用。

    4、对象的比较

    在Java中有两种对象比较的方式,分别为“==”运算符和equals()方法。实质上两种方式有本质的区别。

    public class Compare {
        public static void main(String[] args) {
            
            String s1 = new String("abc");
            String s2 = new String("abc");
            String s3 = s1;
            
            //使用“==”运算符进行比较
            System.out.println(s1 == s2);   //返回false
            System.out.println(s1 == s3);   //返回true
            
            //使用equals()方法进行比较
            System.out.println(s1.equals(s2));   //返回true
            System.out.println(s1.equals(s3));   //返回true
        }
    }
    

    上述代码中,s1和s3引用指向同一对象的内存地址。

    5、对象的销毁

    每个对象都有生命周期,当生命周期结束时,分配给该对象的内存地址将会被回收。在其他语言中需要手动回收废弃的对象,但是Java拥有一套完整的垃圾回收机制,用户不用担心废弃的对象占用内存,垃圾回收器将回收无用的但占用内存的资源。

    在谈到垃圾回收机制之前,首先需要了解何种对象会被Java虚拟机视为垃圾。主要包括以下两种情况:

    1. 对象引用超过其作用范围,这个对象将被视为垃圾。
    {
        Example example = new Example();    //对象example超过其作用范围,将消亡
    }
    
    1. 将对象赋值为null。
    {
        Example example = new Example();
        example = null;    //当对象被置为null时,将消亡
    }
    

    虽然垃圾回收机制已经很完善了,但垃圾回收器只能回收那些由new操作符创建的对象。如果某些对象不是通过new操作符在内存中获取一块内存区域,这种对象可能不能被垃圾回收机制所识别。

    因此,Java中提供了一个finalize()方法,这个方法是Object类的方法,它被声明为protected,用户可以在自己的类中定义这个方法。如果用户在类中定义了finalize()方法,在垃圾回收时会首先调用该方法,在下一次垃圾回收动作发生时,才能真正回收被对象占用的内存。

    说明:有一点需要明确,垃圾回收或finalize()方法不保证一定发生,如Java虚拟机内存耗损殆尽时,它是不会执行垃圾回收的。

    六、实践与练习

    编写一个矩形类,将长和宽作为矩形类的属性,在构造方法中将长和宽初始化,定义一个成员方法求此矩形的面积。

    public class Rectangle {
        //定义成员变量
        double length;
        double width;
    
        //定义成员方法
        public double area(){
            //计算矩形的面积
            double sum = this.length * this.width;
            //返回计算结果
            return sum;
        }
        //定义构造方法
        public Rectangle(){
            this.length = 22.2;    //成员变量初始化
            this.width  = 14.6;    //成员变量初始化
        }
        //主方法
        public static void main(String[] args) {
            //创建对象
            Rectangle rectangle = new Rectangle();
            //调用成员方法
            double result = rectangle.area();
            //打印结果
            System.out.println(result);
        }
    }
    
  • 相关阅读:
    Unity调用Android的API实现分享功能<转>
    CG函数
    Android三种播放视频的方式
    Android 对话框(Dialog)大全 建立你自己的对话框
    Android播放本地视频
    Unity与Android的对调
    [Shader]LOGO闪光效果
    Android实现拍照与打开本地图片
    Android位图相关解码操作
    Unity3d生成二维码
  • 原文地址:https://www.cnblogs.com/xuliang-daydayup/p/12900906.html
Copyright © 2011-2022 走看看