Java复习笔记---对象和类

1类的对象的定义及UML表示

类是对象的模板,对象是类的实例
UML介绍
UML:广泛应用的面向对象设计的的建模工具,但独立于任何具体程序设计语言。作为一种建模语言,UML有严格的语法和语义规范。对于复杂系统,先用UML建模,再编写代码。
UML工具会自动把模型编译成Java(C++)源码(方法体是空的)
UML采用一组图形符号来描述软件模型,这些图形符号简单、直观和规范。所描述的软件模型,可以直观地理解和阅读,由于具有规范性,所以能够保证模型的准确、一致。
在这里插入图片描述
成员访问权限:公有public用+表示,保护protected用#表示,
私有private用-表示,包级用~表示或默认无表示
包级即可以被同一个package的代码访问的成员。Java无friend,无析构函数,垃圾自动回收

2.定义类并用new创建其对象

在这里插入图片描述

int[]a=new int[10];//所有元素缺省值=0
//当创建对象数组时,数组元素的缺省初值为null.
Circle[] circleArray=new Circle[10];//这时没有构造Circle对象,只是构造数组
for(int i = 0; i < circleArray.length; i++) {
    
    
	circleArray[i] = new Circle( );    //这时才构造Circle对象,可使用有参构造函数
}

在这里插入图片描述

3.构造函数

  • 无返回类型,名字同类名,用于初始化对象。
  • 注意JAVA如果定义void className(…),被认为是普通方法
  • 只在new时被自动执行。 必须是实例方法(无static),可为公有、保护、私有和包级权限。
  • 类的变量为引用(相当于C指针),指向实例化好的对象。
  • Circle c2=new Circle(5.0);//调用时必须有括弧,可带参初始化 缺省构造函数(同C++)
    如果类未定义任何构造函数,编译器会自动提供一个不带参数的默认构造函数。
    如果已自定义构造函数,则不会提供默认构造函数。
  • Java没有析构函数,但垃圾自动回收之前会自动调用finalize(),可以覆盖定义该函数(但是finalize调用时机程序员无法控制)。
public class ConstructorTest {
    
    
    //构造函数前面不能有void   
    public ConstructorTest() {
    
    
         System.out.println("constructor");
    }

    //如果和类名同名函数前面加了void(可返回任何类型), 编译器看成是普通函数,这和C++不一样 
    public void ConstructorTest() {
    
    
        System.out.println("normal instance method return void");
    }
    public double ConstructorTest(double d) {
    
    
        System.out.println("normal method return double");
        return d;
    }
    public static void main(String ... args){
    
    
	 //先调用构造,再调用void ConstructorTest() 
        new ConstructorTest().ConstructorTest();
    }
}

4.理解对象访问、向方法传递对象引用

访问对象:通过对象引用访问,JVM 引用计数器, if计数器==0,自动回收
1.非静态数据字段:c2.radius
2.对象的实例方法:c2.findArea()
3.在实例方法中this,代表当前对象(引用当前对象:相当于指针)

public class Circle {
    
    
    double radius = 1.0;

    Circle() {
    
    
	radius = 1.0;
    }

    Circle(double r) {
    
    
        this.radius = r;
    }

    double findArea() {
    
    
        return radius * radius * Math.PI;
    }

    public void setRadius(double newRadius){
    
    
        this.radius = newRadius;
    }
}
public class TestSimpleCircle {
    
    
    public static void main(String[] args){
    
    
        Circle c1 = new Circle();
        System.out.println("Area = " + c1.findArea() + 
                ", radius = " + c1.radius);

        Circle c2 = new Circle(10.0);
        System.out.println("Area = " + c2.findArea() + 
                ", radius = " + c2.radius);

        //modify radius
        c2.setRadius(20.0);
        System.out.println("Area = " + c2.findArea() + 
                ", radius = " + c2.radius);
    }
}


注意:
1.一个对象的引用计数=0时,被自动回收
2.基本类型和引用类型的区别
在这里插入图片描述
图解
包:(package)

  • package本质上就是C++里的namespace,因此
    在同一个package里不能定义同名的标识符(类名,接口名,枚举名)。例如一个类名和一 个接口名不能相同
  • 如果要使用其它包里标识符,有二个办法:
    用完全限定名,例如要调用java.util包里的Arrays类的sort方法: java.util.Arrays.sort(list);
    在package语句后面,先引入要使用其它包里的标识符,再使用:
    import java.util.Arrays; //或者: import java.util.*;
    Arrays.sort(list);
    import语句可以有多条,分别引入多个包里的名字

话不多说,上代码

package p1;
public class A{
    
    }
//另一个文件
package p2;
import p1.*;  //按需导入,没有马上把p1.A引入到当前域

//因此当前文件里可以定义A
public class A {
    
    
    public static void main(String[] args){
    
    
        A a1 = new A();     //这时A是p2.A
        System.out.println(a1 instanceof p2.A); //true

	//当前域已经定义了A,因此要想使用package p1里的A,
	//只能用完全限定名
        p1.A a2 = new p1.A();
    }
}

在这里插入图片描述
包的命名习惯

//包的命名习惯: 将Internet域名作为包名 (但级别顺序相反),这样的好处是避免包名的重复
org.apache.tools.zip
cn.edu.hust.cs.javacourse.ch1
//如果所有程序员都遵循这种包命名的约定,包名重复的可能性就非常小

数据成员的封装

//面向对象的封装性要求最好把实例成员变量设为私有的或保护的
//同时为私有、保护的实例成员变量提供公有的get和set方法。get和set方法遵循JavaBean的命名规范
//设成员为DateType propertyName。
//get用于获取成员值:public DateType getPropertyName( ); 
//set用于设置成员值:public void setPropertyName(DateType value)
class Circle{
    
    
    private double radius=1.0;        //数据成员设为私有
    public Circle( ){
    
     radius=1.0; }
	public double getRadius( ){
    
     return radius; }
    public void setRadius(double r){
    
     radius=r; }
}

5.实例(静态)的变量、常量和方法

class Circle {
    
    
    private double radius;
    /** 私有静态变量,记录当前内存里被实例化的Circle对象个数*/
    private static int numberOfObjects = 0; 

    public Circle() {
    
     radius = 1.0; numberOfObjects++; }
    public Circle(double newRadius) {
    
     radius = newRadius; numberOfObjects++; }
   //每个重载的构造函数计数器+1
    public double getRadius() {
    
    return radius;}
    public void setRadius(double newRadius) {
    
     radius = newRadius;}
    /** 公有静态方法,获取私有静态变量内容*/
    public static int getNumberOfObjects() {
    
    return numberOfObjects;}
   //必须static静态返回
    /** Return the area of this circle */
    public double findArea() {
    
      return radius * radius * Math.PI; }
    @Override
    /*覆盖从Object继承的finalize方法,该方法在对象被回收时调用,方法里对象计数器-1。注意该方法调用时机不可控制。 @Override是注解(annotation)
告诉编译器这里是覆盖父类的方法*/
    public void finalize() throws Throwable {
    
    
        numberOfObjects--; //对象被析构时,计数器减1
        super.finalize();
    }
}

加上注解的好处:
这里加上注解的好处:
1、可以当注释用,方便阅读;
2、编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错。例如,你如果没写@Override,而你下面的方法名又写错了,这时你的编译器是可以编译通过的,因为编译器以为这个方法是你的Anno子类中自己增加的方法。
Java提供了Annotation API让我们自定义注解。

实例变量:对象名.变量名
静态变量:类名.变量名。。被类的所有实例共享
在这里插入图片描述
注意:

  1. 实例常量是没有用static修饰的final变量。
  2. 静态常量是用static修饰的final变量。Math类中的静态常量PI定义为:
    public static final double PI = 3.14159265358979323846;
  3. 所有常量可按需指定访问权限,不能用等号赋值修改。
  4. 由于它们不能被修改,故通常定义为public。
  5. final也可以修饰方法
    final修饰实例方法时,表示该方法不能被子类覆盖(Override) 。非final实例方法可以被子类覆盖(见继承)
    final修饰静态方法时,表示该方法不能被子类隐藏(Hiding)。非final静态方法可以被子类隐藏。
  6. 构造函数不能为final的。

方法重载(Overload)、方法覆盖(Override)、方法隐藏(Hiding)的区别

  • 方法重载:同一个类中、或者父类子类中的多个方法具有相同的名字,但这些方法具有不同的参数列表(不含返回类型,即无法以返回类型作为方法重载的区分标准)
  • 方法覆盖和方法隐藏:发生在父类和子类之间,前提是继承。子类中定义的方法与父类中的方法具有相同的方法名字、相同的参数列表、相同的返回类型(也允许子类中方法的返回类型是父类中方法返回类型的子类)
    –方法覆盖:实例方法
    –方法隐藏:静态方法
    直接coding
public class A {
    
    
    public void m(int x, int y) {
    
    }
    public void m(double x, double y) {
    
    }

//下面语句报错m(int,int)已经定义, 重载函数不能通过返回类型区分
//    public int m(int x, int y) { return 0;}; 
}
class B extends A{
    
     //B继承了A
    public void m(float x, float y) {
    
     } //重载了父类的m(int,int)和m(double,double)
    public void m(int x, int y) {
    
    } //覆盖了父类的void m(int,int),注意连返回类型都必须一致

    //注意下面这个语句报错,既不是覆盖(与父类的void m(int,int)返回类型不一样)
    // 也不是合法的重载(和父类的m(int,int)参数完全一样,只是返回类型不一致
//    public int m(int x, int y) {} //错误
    
    //子类定义了新的重载函数int m()
    public int m(){
    
    return 0;};
}

方法的注意事项:

  • 静态方法(static method)是用static修饰的方法。
  • 构造函数不能用static修饰,静态函数无this引用。
  • 每个程序必须有public static void main(String[])方法。
    JOptionPane.showMessageDialog
    JOptionPane.showInputDialog
    Math.random
  • 静态方法可以通过对象或类名调用。
  • 静态方法内部只能访问类的静态成员 (因为实例成员必须有实例才存在,当通过类名调用静态方法时,可能该类还没有一个实例)
  • 静态方法没有多态性。

6.可见性修饰符

在这里插入图片描述
private: 只能被当前类定义的函数访问。
包级:无修饰符的成员,只能被同一包中的类访问。
protected:子类、同一包中的类的函数可以访问。
public: 所有类的函数都可以访问。

package p1;
public class C1{
    
    //在C1.java
   public int x=1;//public
   int y=2;//包级
   protected int u=3,w=4;//
   private int z;
   public void m1(){
    
     
      int i = x = u;
      int j = y = w;
      int k = z;
      m2();
      m3(); 	
   } 
   void m2(){
    
     } //包级
   private void m3(){
    
     }
}
public class C2 extends C1{
    
    
//在C2.java
   int u=5; //包级
   void aMethod(){
    
    
      C1 o = new C1( );//ok,C1是public
      int i = o.x;//ok, x是public
      int j = o.y;//ok,y(包级),可在同一包内访问
      int h = o.u;//ok,u(保护)可在同一包内访问
      i=u+super.u;//ok,本类u及super.u(父类保护)
      int k = o.z;//error,z是私有的 
      o.m1(); //ok, m1是public
      o.m2(); //ok, m2无访问修饰,可在同一包内访问
      o.m3(); //error, m3是私有的            
   }
}
//
public class C3 extends C1{
    
     //C3.java
   int u=5;
   void aMethod(){
    
    
      C1 o =new C1( );//ok,C1是public
      int i = o.x; //ok, x 是public
      int j = o.y;//error,y(包级),不能在不同包内访问
      int h = o.u;//error,u(保护,当前对象非o子类对象),不能在不同包内访问
      i=u+super.u;//ok,本类u及super.u(保护,当前对象是父类super的子类对象)
      int k = o.z;//error,z是私有的 
      o.m1(); //ok, m1是public
      o.m2(); //error, m2(包级),不能在不同包内访问
      o.m3(); //error, m3是私有的            
   }
}

注意:访问控制针对的是类型而不是对象级别

public class Foo{
    
    
   private boolean x;
   
   public void m(){
    
    
      Foo foo = new Foo();

      //因为对象foo在Foo类内使用,所以可以访问私有成员x
      boolean b = foo.x //ok
   }   
}
public class Test{
    
      
   public static void main(String[] args){
    
    
      Foo foo = new Foo();

      //因为对象foo在Foo类外使用,所以不可以访问foo的私有成员x
      boolean b = foo.x //error
   }   
}

特殊说明:

  • 大多数情况下,构造函数应该是公有的
  • 有些特殊场合,可能会防止用户创建类的实例,这可以通过将构造函数声明为私有的来实现。
    —例如,包java.lang中的Math类的构造函数为私有的,所有的数据域和方法都是静态的,可以通过类名直接访问而不能实例化Math对象。
    private Math(){ }

7.类成员变量的作用域和访问优先级

类的成员变量(实例变量和静态变量)的作用域是整个类,与声明的位置无关。
如果一个成员变量的初始化依赖于另一个变量,则另一个变量必须在前面声明。

public class Foo {
    
    
	int i;//成员变量默认初始化,new后成员默认值为0或null,函数局部变量须初始化
	int j = i + 1;
    int f( ){
    
     int i=0; return i+this.i; } //局部变量i会优先访问
}   //作用域越小,被访问的优先级越高

如函数的局部变量i与类的成员变量i名称相同,那么优先访问局部变量i,成员变量i被隐藏(可用this.i或类名.i发现)。

8 this引用

this引用指向调用某个方法的当前对象
在实例方法中,实例变量被同名局部变量或方法形参隐藏,可以通过this.instanceVariable访问实例变量。
调用当前类的其它构造函数,需防止递归调用。
this(actualParameterListopt)
必须是构造函数的第1条语句。

class Foo{
    
    
   int i = 5;
   static double k = 0.0;
   void setI(int i){
    
    
      this.i= i;
   }
   static void setK(int k){
    
    
      Foo.k = k;
   }
}


class Foo{
    
    
   int i = 5;
   static double k = 0.0;
   void setI(int i){
    
    
      this.i= i;
   }
   static void setK(int k){
    
    
      Foo.k = k;
   }
}

说明:成员变量i、k的初始值可被构造函数Foo( )修改,但编译提供的默认构造函数FOO( )什么也没有做。

猜你喜欢

转载自blog.csdn.net/wangtanCSDN/article/details/108782095