今天处理了一些面向对象的基本练习题,针对于面向对象解决问题的思路和方法加以理解和消化,回顾并复习了面向对象编程的基本内容。还学习了静态关键词Static的使用方法和特点
1.面向对象编程语言的基本特征是什么
面向对象的三大基本特征是:封装、继承、多态
- 封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据。
- 继承是为了重用父类代码,同时为实现多态性作准备。
- 多态性是发送消息给某个对象,让该对象自行决定响应何种行为。通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。
2.设计一个包含多个构造器的类,并分别用这些构造函数实例化对
象
- 构造器方法不能有返回值类型的声明,即使void都不可以
- public void A(){}不是构造器,是合法的普通方法,可以直接调用 new A().A()
public class A {
public A() {
} // new A();
//允许构造器方法重载---方法名称相同,参数不同
public A(int k) {
} // new A(123)
public A(String k) {
} // new A("yan")
public A(int k1, String k2) {
} // new A(123,"yan")
public A(String k1, int k2) {
} // new A("yan",123)
}
3.Java语言中,成员变量与局部变量有什么区别
- 成员变量定义在类中,整个类中都能访问,和具体的定义位置无关
- 局部变量定义在方法、语句、局部代码块中,只在所属的区域中有效,而且必须是先定义后使用,必须是先赋初值后使用
- 成员变量存在于堆内存的对象中,堆内存中的对象大小可以任意,并允许在运行时进行调整。所以访问查找的效率比较低
- 局部变量存在与栈内存的方法中,可以快速定位,但是大小是限定的
- 成员变量随着对象的创建而存在,随着对象的表示而消失。局部变量随着所属区域的执行而存在,随着所属区域的结束而释放
- 成员变量都有默认初始化值;局部变量没有默认初始化值
4.编写一个Cal1类实现加减两种运算,然后编写另一个Cal2类实现乘除两种运算
public class Cal1 {
private int n1, n2; // 省略了get/set方法
public long add()
{
// 不会出现空指针问题,因为n1和n2是简单类型,默认值为0
return 0L + n1 + n2;
}
public long sub() {
return 0L + n1 - n2;
}
}
//Cal1 c1=new Cal1(); c1.setN1(100); c1.setN2(200); long res=c1.add();
public class Cal2 extends Cal1 {
public double mul() {
return 1. * getN1() * getN2();
}
public double div() {
if (getN2() != 0)
return 1. * getN1() / getN2();// 如果没有1.这个操作,则是整除计算
else
return Double.POSITIVE_INFINITY; // 正负无穷大之分
}
}
5.建立3个类:居民、成人、官员。居民包含身份证号、姓名、出生日期,而成人继承自居民,多包含学历、职业两项数据,官员则继承自成人,多包含党派、职务两项数据。要求每个类的字段都以属性的方式对外提供数据输入输出的功能
所谓的字段一般用于指代只有属性定义,但是没有get/set方法;属性表示既有对应的private属性声明,同时又对应的get/set方法
public class Resident {
private String idCard;// 18位并且可能有字符,数值型数据的比较效率高于字符串
private String name;
private Date birth; // java.util.Date是系统提供的日期类型
//get/set方法....
public void setIdCard(String idCard) {
//针对传入的身份证号码进行合法性验证,如果数据不合法应该通过异常告知调用处或者赋默认值
if (idCard != null && idCard.trim().length() == 18)
this.idCard = idCard;
else
throw new RuntimeException("身份证号码不合法!");
}
}
public class Adult extends Resident {
private String education;// 真实实现应该考虑使用枚举类型enum,以保证education的取值范围
private String vocation;// 职业
//get/set方法....
}
public class Official extends Adult {
private String party;
private String duties;
//get/set方法....
}
6.编写一个控制台应用程序,完成下列功能,并回答提出的问题
- 创建一个类A,在构造函数中输出“A”,再创建一个类B,再构造函数中输出“B”
- 从A继承一个名为C的新类,并在C中创建一个成员变量B,不要为C创建构造函数
- main方法中创建类C的一个对象,写出运行程序后输出的结果
- 如果在C中也创建一个构造函数输出"C",整个程序运行的结果是什么
//一个文件中可以定义多个类,但是只能有一个public class
//public class到处可见,默认class只能同包可见
public class A {
public A() {
System.out.println("A");
}
}
class B {
public B() {
System.out.println("B");
}
}
class C extends A {
private B b = new B();
public C() {
System.out.println("C");
}
}
class Test {
public static void main(String[] args) {
C c = new C();// 输出结果为先A后B
}
}
6.创建一个类,它存储一个int数据成员,当该数据成员被存储时将其乘以100,当其被读取时将其除以100
public class A {
private int data;
public void setData(int data) {
if (data <= Integer.MAX_VALUE / 100)
this.data = data * 100;
else
throw new RuntimeException("数据太大了!");
}
public int getData() {
return this.data / 100;
}
}
定义常量
命名规则:名称全大写,下划线分词
- 声明语法:public static final double MIN_NUMBER = 0.1;
- 声明的同时直接进行初始化
public static final double MIN_NUMBER = 0.1; - 先声明后在static静态块中赋值
public static final double MIN_NUMBER;
static{
MIN_NUMBER = 0.1;
}
静态方法
因为可以直接使用”类名.方法名”的形式直接调用静态方法,静态方法执行时很有可能并没有构建对象,所以在静态方法中不允许使用this/super之类用于指定对象的关键字
public class B {
private Random random;
public static void pp() {
System.out.println("pp....");
//静态方法的调用没有要求必须构建B对象,所以很有可能random根本不存在,要求在静态方法中不能访问非静态成员
// System.out.println(random);
// 在static方法中不允许出现this或者super关键字
}
public static void main(String[] args) {
B.pp();// 调用当前类中的static方法,可以直接写方法名称pp()
}
}
当然在静态方法中允许创建对象,并调用对象方法
静态方法只能直接访问静态成员,不能直接访问非静态成员
静态块
public class A {
static {
// 类内,所有方法之外
System.out.println("静态代码块");
}
}
- 类在执行时需要通过一个叫作类加载器的组件将程序加载到内存中,类在运行时一般不会发生变化,所以类不会频繁加载,在整个运行过程中只加载一次,而且常驻内存
- 静态块在类加载完毕后自动执行,而且只执行一次
public class B{
static {
System.out.println("static...");
}
public static void main(String[] args) {
}
}
非静态块
非静态块在类内且在所有的方法之外,非静态块并不会在类加载后自动执行,而是在构建当前对象时自动执行。new一次则会执行一次,执行时机在构造器之前执行
public class A1 {
public static B1 bb = new B1();
static {
//当类加载完毕自动执行,在整个类的使用过程中只执行一次
//static块和静态属性执行的优先级相同,谁定义时在前先执行谁
System.out.println("A1.static");
}
{
//在构建对象时自动执行,在这个对象的生命周期中执行且只执行一次
System.out.println("A1 non static");
}
//非静态代码块和非静态属性在构造器之前执行
public A1() {
System.out.println("this is A1...");
}
public C1 cc = new C1();
public static void pp() {
//静态方法只能直接访问静态成员
// System.out.println(age);
A1 aa = new A1();
System.out.println(aa.cc);
//不能出现this和super
// System.out.println(this);
System.out.println(bb);
System.out.println("this is A1.pp...");
}
public static void main(String[] args) {
// A1.pp();或者 pp();
A1 aa = new A1();
A1 aa1 = new A1();
A1 aa2 = new A1();
}
}
class B1 {
public B1() {
System.out.println("this is B1...");
}
}
class C1 {
public C1() {
System.out.println("this is C1...");
}
}
- 当类加载完毕会自动优先处理static属性和static块,这两个优先级是相同的,所以谁在前先处理谁
- new对象时,处理非静态属性和非静态块,这两个优先级是相同的,所以谁在前先处理谁
- 最后执行构造器
使用注意事项
- 静态方法只能访问静态成员,静态有访问局限性
- 静态方法中不能有this super关键字
- 主函数是静态的
什么时候使用静态
- 当成员变量的数据在当前类的各个对象都相同时,可以用static修饰的,让多个对象共享
- 非静态方法的调用没特殊要求,构建对象后即可调用;静态方法可以通过类名称直接调用,无需创建对象
- 非静态方法可以使用静态或者非静态成员;静态方法只能访问静态成员
- 如果类中的功能都是静态的,那么该类创建对象是没有意义的,所以构造方法需要私有化
static相关问题
父类中定义的静态方法(类方法)是否可以通过”子类名.静态方法名”的方式进行调用?
可以,因为继承的特性
public class Test1 {
public static void main(String[] args) {
Son.pp();
}
}
class Fa {
public static void pp() {
System.out.println("Fa.pp()");
}
}
class Son extends Fa {
}
父类中定义的静态方法(类方法)是否可以在子类中进行覆盖定义(重写)?
可以,但是方法签名必须一致,方法必须为static定义
public class Test1 {
public static void main(String[] args) {
Son.pp();
}
}
class Fa {
public static void pp() {
System.out.println("Fa.pp()");
}
}
class Son extends Fa {
public static void pp() {
System.out.println("Son.pp()");
}
}