博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 06类及成员2

Posted on 2020-10-26 08:28  Kingdomer  阅读(102)  评论(0)    收藏  举报

JavaEE - 06类及成员2

(1)关键字: static

(1.1)static 概念

  • 当编写一个类时,其实是在描述其对象的属性和行为,而并没有产生实质上的对象,
    • 只有通过new关键字才会产出对象,系统分配内存空间给对象,其方法才可以供外部调用。
  • 有时候希望无论是否产生了对象或产生多少对象情况下,某些特定数据在内存空间里只有一份。 
    • 每一个中国人的国家名称,没必要每个对象都单独分配一个代表国家名称的变量。

(1.2)static关键字

  • static可以用来修饰: 属性、方法、代码块、内部类
  • 使用static修饰属性: 静态变量
    • 属性: 按照是否使用static修饰,分为 静态属性(静态变量) vs 非静态属性(实例变量)
    • 实例变量:创建类的多个对象,每个对象都独立拥有一套类中的非静态属性。
      • 当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值的修改。
    • 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。
      • 当通过某一个对象修改静态变量,会导致其他对象调用此静态变量时,是修改过了的。
  • 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用。
  • 静态变量的加载早于对象的创建。
  • 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
  • 调用关系: 类可以调用 类变量,不可以调用实例变量;  对象可以调用 类变量 和实例变量。
  • 静态方法
    • 静态方法中,只能调用静态的方法或属性;非静态方法中,既可以调用非静态方法的方法或属性,也可以调用静态的方法或属性。
    • 在静态方法中,不能使用this、super关键字。
  • 关于静态属性和静态方法的使用,从生命周期的角度去理解。
  • 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
    • 操作静态属性的方法,通常设置为static的。工具类中的方法,习惯上声明为static的。

 

 

public class Circle {
    private double radius;
    private int id;

    public Circle(){
        id = init++;
        total++;
    }

    public Circle(double radius){
        this();
        this.radius = radius;
//        id = init++;
//        total++;
    }

    private static int init = 1001; // static声明的属性被所有对象共享
    private static int total; // 记录创建的圆的个数

    public double findArea(){
        return 3.14 * radius * radius;
    }

    public int getId(){
        return id;
    }
    public int getTotal(){
        return total;
    }
}

 

 

public class CircleTest {
    public static void main(String[] args) {
        Circle c1 = new Circle(3);
        System.out.println(c1.findArea());
        Circle c2 = new Circle(4);
        System.out.println(c2.findArea());
        System.out.println(c1.getId());    // 1001
        System.out.println(c2.getId());    // 1002
        System.out.println(c1.getTotal()); // 2
        System.out.println(c2.getTotal()); // 2
    }
}

 

 

 

 

(2)理解main方法的语法

  • main()方法的使用说明:
    • main()方法作为程序的入口。
    • main()方法也是一个普通的静态方法。
    • main()方法可以作为我们与控制台交互的方式。
 
public class MainTest {
    public static void main(String[] args) {  //入口
        Main.main(new String[3]);
        MainTest test = new MainTest();
        test.show();
    }

    public void show(){
        System.out.println("MainTest show......");
    }
}


class Main{
    public static void main(String[] args) {
        for(int i =0 ; i< args.length; i++){
            args[i] = "args_" + i;
            System.out.println(args[i]);
        }
    }
}

 

 
 
public class MainTest {
    public static void main(String[] args) {  //入口
        for(int i =0 ; i< args.length; i++){
            System.out.println("********" + args[i]); //********78
            int num = Integer.parseInt(args[i]);
            System.out.println("*****" + num); //*****78
        }
    }
}

 

 

 

E:\JavaProgramLearn\JavaEE\06Classkeyboard\src\com\bearpx\mainMethod>javac MainTest.java

E:\JavaProgramLearn\JavaEE\06Classkeyboard\src\com\bearpx\mainMethod>java MainTest 22
错误: 找不到或无法加载主类 MainTest

E:\JavaProgramLearn\JavaEE\06Classkeyboard\src\com\bearpx\mainMethod>java com.bearpx.mainMethod.MainTest
错误: 找不到或无法加载主类 com.bearpx.mainMethod.MainTest

// 因为开发时,使用了包的方式,到相应路径后,使用完整的包路径
E:\JavaProgramLearn\JavaEE\06Classkeyboard\src>java com.bearpx.mainMethod.MainTest "22"
********22
*****22

 

 

 

(3)代码块(初始化快)

  • 代码块的作用: 用来初始化类、对象
  • 代码块如果有修饰的话,只能使用static。
  • 分类: 静态代码块 vs 非静态代码块
  • 静态代码块
    • 内部可以有输出语句
    • 随着类的加载而执行,而且只执行一次
    • 作用:初始化类的信息
    • 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
    • 静态代码块的执行要优先于非静态代码块的执行
  • 非静态代码块
    • 内部可以有输出语句
    • 随着对象的创建而执行
    • 每创建一个对象,就执行一次非静态代码块
    • 作用:可以在创建对象时,为对象的属性等进行初始化
    • 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
  • 静态代码块内只能调用静态属性、静态方法,不能调用非静态的结构
  • 非静态代码块内可以调用静态属性、静态方法、非静态属性、非静态方法

public class Person {

    String name;
    int age;
    static String desc = "我是一个人";

    public Person(){}
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    // 静态代码块
    static {
        System.out.println("hello, static block");
        desc = "我是一个爱学习的人";
    }
    // 代码块
    {
        System.out.println("hello, block");
        age = 1;
    }

    public void eat(){
        System.out.println("吃饭");
    }
}
public class BlockTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
    }
}
//-----------------------------
hello, static block
hello, block
hello, block

 

 

    // 静态代码块
    static {
        System.out.println("hello, static block-2");
    }
    static {
        System.out.println("hello, static block-1");
        desc = "我是一个爱学习的人";
        info();
//        eat();
//        name = "Tom";
    }
// 代码块 { System.out.println("hello, block -1"); age = 1; eat(); desc ="我是一个学习好的人"; info(); } { System.out.println("hello, block -2"); }

 

hello, static block-2
hello, static block-1
我是一个爱学习的人  // static代码块info()方法输出的内容
hello, block -1  // -- 第一个Person对象p1 : 第一个代码块
吃饭              // -- 第一个Person对象p1 : eat()方法
我是一个学习好的人  // -- 第一个Person对象p1 : 代码块info()方法输出内容
hello, block -2  // -- 第一个Person对象p1 : 第二个代码块的 
hello, block -1  // 第二个Person对象p2
吃饭
我是一个学习好的人
hello, block -2

 

 

 

public class DataSourceTest {
    private static DataSource dataSource = null;
    static {
        InputStream is = null;
        try{
            is = DataSourceTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
            Properties props = new Properties();
            props.load(is);
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static Connection getConnection() throws SQLException {
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
        return conn;
    }
}

 

 

代码块执行顺序:   由父及子, 静态先行

class Root {
    static {
        System.out.println("Root的静态代码块");
    }
    {
        System.out.println("Root的普通初始化代码块");
    }
    public Root(){
        System.out.println("Root的无参数的构造器");
    }
}

class Mid extends Root{
    static {
        System.out.println("Mid的静态代码块");
    }
    {
        System.out.println("Mid的普通初始化代码块");
    }
    public Mid(){
        System.out.println("Mid的无参数的构造器");
    }
    public Mid(String msg){
        this();
        System.out.println("Mid的带参数构造器,参数值为:" + msg);
    }
}

class Order extends Mid{
    static {
        System.out.println("Order的静态代码块");
    }
    {
        System.out.println("Order的普通初始化代码块");
    }
    public Order(){
        super("熊");
        System.out.println("Order的无参数的构造器");
    }
}
public class OrderTest {
    public static void main(String[] args) {
        new Order();
    }
}
Root的静态代码块
Mid的静态代码块
Order的静态代码块
Root的普通初始化代码块
Root的无参数的构造器
Mid的普通初始化代码块
Mid的无参数的构造器
Mid的带参数构造器,参数值为:熊
Order的普通初始化代码块
Order的无参数的构造器

 

 
 
class Father {
    static {
        System.out.println("1111111111111");
    }
    {
        System.out.println("222222222222222");
    }
    public Father(){
        System.out.println("3333333333333333");
    }
}
public class Son extends Father{

    static {
        System.out.println("444444444444");
    }
    {
        System.out.println("5555555555555555");
    }
    public Son(){
        System.out.println("666666666666666");
    }

    public static void main(String[] args) {
        System.out.println("77777777777777");
        System.out.println("*************");
        new Son();
        System.out.println("--------------");
    }
}

 

1111111111111
444444444444
77777777777777
*************
222222222222222
3333333333333333
5555555555555555
666666666666666
--------------

 

 

 

 
对属性可以赋值的位置:
1. 默认初始化
2. 显示初始化
3. 构造器中初始化
4. 有了对象以后,通过"对象.属性"或"对象.方法"的方式进行赋值
5. 在代码块中赋值
执行的先后顺序:  1 - 2/5 - 3 - 4

 

 

 
 

 

(4)关键字: final

  • final可以用来修饰的结构: 类、方法、变量。
  • final用来修饰一个类: 此类不能被其他类继承。如: String类、 System类、 StringBuffer类。
  • final用来修饰方法,表明此方法不可以被重写。如: Object类中的getClass()
  • final修饰变量,此时的"变量"称为一个常量。
  • final修饰属性:可以考虑赋值的位置有: 显示初始化、代码块中初始化、构造器中初始化
  • final修饰局部变量:  使用final修饰形参时,表明此形参是一个常量。
    • 当调用方法时,给常量形参赋一个实参,一旦初始化以后,只能在方法体内使用此形参,但不能重新赋值。
  • static final 用来修饰属性: 全局常量
public class FinalTest {
    final int WIDTH = 0;
    final int LEFT;
    final int RIGHT;
    {
        LEFT = 1;
    }
    public FinalTest(){
        RIGHT = 2;
    }
    public FinalTest(int n){
        RIGHT = n;
    }
}

 

 

   public void show(){
        final int NUM = 10; //常量
        System.out.println(NUM);
    }

    public void display(final int num){
//        num = 20; //Cannot assign a value to final variable 'num'
        System.out.println(num);
    }

 

 

final故障排错1

public class Something {
    public int addOne(final int x){
//        return ++x; //不能分配最终参数x
        return x + 1;
    }

    public static void main(String[] args) {
        Something st = new Something();
        int rs = st.addOne(2);
        System.out.println(rs); // 3
    }
}

 

final故障排错2

class Other {
    public int i;
}

public class Something2 {
    public static void main(String[] args) {
        Other o = new Other();
        new Something2().addOne(o);
        System.out.println(o.i);  // 1
    }

    public void addOne(final Other o){
//        o = new Other(); //Cannot assign a value to final variable 'o'
        o.i++;
    }
}

 

 

final, finally, finalize 的区别?
final
用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.

finally
是异常处理语句结构的一部分,表示总是执行.

finalize
是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. JVM不保证此方法总被调用.

 

 

 

 

 

 

 

 

  继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后,就可以对其中的方法进行重写,那怎么解决呢?

  要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。

 

1.1 final的特点

final修饰类不可以被继承,但是可以继承其他类。

class Yy {}

final class Fu extends Yy{}  //可以继承Yy类

class Zi extends Fu{}     //不能继承Fu类

 

final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖/重写后可以加final。

class Fu {
   // final修饰的方法,不可以被覆盖,但可以继承使用
    public final void method1(){}
    public void method2(){}
}

class Zi extends Fu {
   //重写method2方法
  public final void method2(){}
}

 

 

final修饰的变量称为常量,这些变量只能赋值一次。

final int i = 20;

i = 30;     //赋值报错,final修饰的变量只能赋值一次

 

引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。

final Person p = new Person();

Person p2 = new Person();

p = p2;       //final修饰的变量p,所记录的地址值不能改变

p.name = "小明";  //可以更改p对象中name属性值

          

p不能为别的对象,而p对象中的name或age属性值可更改。

 

修饰成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。)

class Demo {
  //直接赋值   final int m = 100;
  //final修饰的成员变量,需要在创建对象前赋值,否则报错。   final int n;   public Demo(){     //可以在创建对象时所调用的构造方法中,为变量n赋值     n = 2016;   } }

  

 

2.static概念

  当在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是通过创建本类对象调用的。当在调用对象的某个方法时,这个方法没有访问到对象的特有数据时,方法创建这个对象有些多余。可是不创建对象,方法又调用不了,这时就会想,那么我们能不能不创建对象,就可以调用方法呢?

  可以的,我们可以通过static关键字来实现。static它是静态修饰符,一般用来修饰类中的成员。

2.1 static特点

  被static修饰的成员变量属于类,不属于这个类的某个对象。(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)。

代码演示:

class Demo {

  public static int num = 100;

}

class Test {

  public static void main(String[] args) {

    Demo d1 = new Demo();

    Demo d2 = new Demo();

    d1.num = 200;

    System.out.println(d1.num); //结果为200

    System.out.println(d2.num); //结果为200

  }

}

 

被static修饰的成员可以并且建议通过类名直接访问。 

访问静态成员的格式:

  类名.静态成员变量名

  类名.静态成员方法名(参数)

  对象名.静态成员变量名     ----不建议使用该方式,会出现警告

  对象名.静态成员方法名(参数)  ----不建议使用该方式,会出现警告

2.2 static注意事项

静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。

  同一个类中,静态方法只能访问静态成员

class Demo {

  public int num = 100;      //成员变量

  public static int count = 200; //静态成员变量

  //静态方法

  public static void method(){

    //System.out.println(num); 静态方法中,只能访问静态成员变量或静态成员方法

    System.out.println(count);

  }

}

 

2.3 定义静态常量

开发中,我们想在类中定义一个静态常量,通常使用public static final修饰的变量来完成定义。此时变量名用全部大写,多个单词使用下划线连接。

定义格式:  public static final 数据类型 变量名 = 值;

class Hello {

  public static final String SAY_HELLO = "你好,世界";

  public static void method(){

    System.out.println("这是一个静态方法。");

  }

}

 

当我们想使用类的静态成员时,不需要创建对象,直接使用类名来访问即可。

  System.out.println(Hello.SAY_HELLO); //打印

  Hello.method(); // 调用一个静态方法

 

注意:

接口中的每个成员变量都默认使用public static final修饰。

所有接口中的成员变量已是静态常量,由于接口没有构造方法,所以必须显示赋值。可以直接用接口名访问。

interface Inter {

  public static final int COUNT = 100;

}

访问接口中的静态变量: Inter.COUNT

 

3.关于private的描述 

了解到封装在生活的体现之后,又要回到Java中,细说封装的在Java代码中的体现,先从描述Person说起。

描述人: Person

属性:年龄。

行为:说话:说出自己的年龄。

class Person {

  int age;

  String name;

  public void show() {

    System.out.println("age=" + age + ",name" + name);

  }

}

 

public class PersonDemo {

  public static void main(String[] args) {

    // 创建Person对象

    Person p = new Person();

    p.age = 30; // 给Person对象赋值

    p.name = "张三";

    p.show(); // 调用Person的show方法

  }

}

 

通过上述代码发现,虽然我们用Java代码把Person描述清楚了,但有个严重的问题,就是Person中的属性的行为可以任意访问和使用。这明显不符合实际需求。

可是怎么才能不让访问呢?需要使用一个Java中的关键字也是一个修饰符 private(私有,权限修饰符)。只要将Person的属性和行为私有起来,这样就无法直接访问。

 

class Person {

  private int age;

  private String name;

  public void show() {

    System.out.println("age=" + age + ",name" + name);

  } 

}

 

年龄已被私有,错误的值无法赋值,可是正确的值也赋值不了,这样还是不行,那肿么办呢?按照之前所学习的封装的原理,隐藏后,还需要提供访问方式。只要对外提供可以访问的方法,让其他程序访问这些方法。同时在方法中可以对数据进行验证。

 

一般对成员属性的访问动作:赋值(设置 set),取值(获取 get),因此对私有的变量访问的方式可以提供对应的 setXxx或者getXxx的方法。

 

class Person {

 

// 私有成员变量

 

private int age;

 

private String name;

 

 

 

// 对外提供设置成员变量的方法

 

public void setAge(int a) {

 

// 由于是设置成员变量的值,这里可以加入数据的验证

 

if (a < 0 || a > 130) {

 

System.out.println(a + "不符合年龄的数据范围");

 

return;

 

}

 

age = a;

 

}

 

 

 

// 对外提供访问成员变量的方法

 

public void getAge() {

 

return age;

 

}

 

}

 

总结:

 

类中不需要对外提供的内容都私有化,包括属性和方法。

 

以后再描述事物,属性都私有化,并提供setXxx getXxx方法对其进行访问。

 

注意:私有仅仅是封装的体现形式而已。