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 --------------
(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方法对其进行访问。
注意:私有仅仅是封装的体现形式而已。
浙公网安备 33010602011771号