第13天 面向对象
继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后,就可以对其中的方法进行重写,那怎么解决呢?
要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。
class Yy {}
final class Fu extends Yy{} //可以继承Yy类
class Zi extends Fu{} //不能继承Fu类
class Fu {
// final修饰的方法,不可以被覆盖,但可以继承使用
public final void method1(){}
public void method2(){}
}
class Zi extends Fu {
//重写method2方法
public final void method2(){}
}
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;
}
}
如:
final Person p = new Person();
p不能为别的对象,而p对象中的name或age可更改。
//final修饰类不能继承
public /*final*/ class Person {
//修饰成员变量,需要在创建对象前赋值,否则报错
private final String name;
//需要在创建对象前赋值
public Person(){
this.name = "";
}
//用带参构造方法为name赋值
public Person(String name){
this.name = name;
}
//final修饰的方法不能被重写
public /* final*/ void method(){
System.out.println("父类方法");
}
public String getName() {
return name;
}
//public void setName(String name) {
//final修饰的变量不能2次赋值
//this.name = name;
//}
}
public class Student extends Person{
@Override
public void method(){
System.out.println("子类方法");
}
}
/*
* final 最终修饰符
*
* 修饰类代表类不能被继承
* 修饰方法不能被重写
* 修饰变量 不能被二次赋值,是 常量
*/
public class Test {
public static void main(String[] args) {
//修饰变量 不能被二次赋值,是 常量
//final int a = 10;
final double PI = 3.141592653589;
System.out.println(PI*2);
final Person p = new Person();//0x1111
//p.setName("柳岩");
//p.setName("金莲");
System.out.println(p.getName());
//引用类型的变量值为对象地址值,地址值不能更改,但是地址内内容可以修改。
//p = new Person();//0x1234
}
}
当在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是通过创建本类对象调用的。当在调用对象的某个方法时,这个方法没有访问到对象的特有数据时,方法创建这个对象有些多余。可是不创建对象,方法又调用不了,这时就会想,那么我们能不能不创建对象,就可以调用方法呢?
可以的,我们可以通过static关键字来实现。static是静态修饰符,一般修饰成员。被static修饰的成员属于类,不属于单个这个类的某个对象。
被static修饰的成员可以并且建议通过类名直接访问。也可以通过某个对象访问到属于类的静态成员,多个对象共享使用同一个静态成员。
static是静态修饰符,一般修饰成员。被static修饰的成员属于类,不属于单个这个类的某个对象。
static修饰的成员被多个对象共享。
static修饰的成员属于类,但是会影响每一个对象。
被static修饰的成员又叫类成员,不叫对象的成员。
如下例中国籍变量,所有中国人国籍均应该为中国,不应各自定义各自的国籍,所以可以将国籍定义为static,属于类,被多个对象共享。
被static修饰的成员可以并且建议通过类名直接访问。也可以通过某个对象访到属于类的静态成员,原因即多个对象均属于一个类,共享使用同一个静态成员。
格式:
类名.静态成员变量名
类名.静态成员方法名(参数)
对象名.静态成员变量名 ------不建议,出现警告
对象名.静态成员方法名(参数) ------不建议,出现警告
代码演示:
class Demo {
//静态成员变量
public static int num = 100;
//静态方法
public static void method(){
System.out.println("静态方法");
}
}
class Test {
public static void main(String[] args) {
System.out.println(Demo.num);
Demo.method();
}
}
通常使用public static final来修饰某个类中的静态常量。此时标识符用全部大写,多个单词使用下划线连接。
格式: 类名.静态成员变量名
如:
定义包含静态成员的的类。
public class Company{
public static final String COMPANY_NAME = “顺风通不通快递公司”;
public static void method(){
System.out.println(“一个静态方法”);
}
}
使用类的静态成员不需要创建对象,直接使用类名即可。
System.out.println(Company. companyName); //打印顺风通不通快递公司
Company.method(); //调用一个静态方法
接口中的每个成员变量都默认使用public static final修饰,所有接口中的成员变量都是静态常量,由于接口没有构造方法,所以必须显示赋值。可以直接用接口名访问。
/*
* 被static修饰的成员属于类,不属于单个这个类的某个对象。
*/
public class Person {
static int age;
private String name;
//ctrl+shift+x,y大小写切换
public static final String ADDRESS ="中国";
// 静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。
public static void method(){
//System.out.println(ADDRESS);
System.out.println("我是一个静态方法");
System.out.println(age);
//不能使用this/super
//System.out.println(this.age);
//静态只能访问静态 不能用已存在的去访问还没出现的 静态内容优于对象存在
//System.out.println(name);
}
//main方法为静态方法仅仅为程序入口,不属于任何一个对象,所以可以定义在任意类中。
public static void main(String[] args){
method();
}
}
/*
* static静态修饰符
*
* 格式:
* 类名.静态成员变量名
* 类名.静态成员方法名(参数)
* 对象名.静态成员变量名 ------不建议,出现警告
* 对象名.静态成员方法名(参数) ------不建议,出现警告
*/
public class Test {
public static void main(String[] args) {
// System.out.println(Person.age);
//
// Person.method();
Person p = new Person();
p.age = 18;
System.out.println(p.age);
Person p2 = new Person();
System.out.println(p2.age);
System.out.println(Person.ADDRESS);
}
}
匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。
如:已经存在的类:
public class Person{
public void eat(){
System.out.println();
}
}
创建一个普通对象
Person p = new Person();
创建一个匿名对象
new Person();
new Person().eat() //eat方法被一个没有名字的Person对象调用了。
new Person().eat(); 创建一个匿名对象,调用eat方法
new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象
class Demo {
public static Person getPerson(){
//普通方式
//Person p = new Person();
//return p;
//匿名对象作为方法返回值
return new Person();
}
public static void method(Person p){}
}
class Test {
public static void main(String[] args) {
//调用getPerson方法,得到一个Person对象
Person person = Demo.getPerson();
//调用method方法
Demo.method(person);
//匿名对象作为方法接收的参数
Demo.method(new Person());
}
}
将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。其他类也称为外部类。
在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。
class 汽车 { //外部类
class 发动机 { //内部类
}
}
内部类分为成员内部类与局部内部类。
我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符、继承与实现关系等。在内部类中可以直接访问外部类的所有成员。
成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问
class 外部类 {
修饰符 class 内部类 {
//其他代码
}
}
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
class Body {//外部类,身体
private boolean life= true; //生命状态
public class Heart { //内部类,心脏
public void jump() {
System.out.println("心脏噗通噗通的跳")
System.out.println("生命状态" + life); //访问外部类成员变量
}
}
}
public static void main(String[] args) {
//创建内部类对象
Body.Heart bh = new Body().new Heart();
//调用内部类中的方法
bh.jump();
}
局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问
class 外部类 {
修饰符 返回值类型 方法名(参数) {
class 内部类 {
//其他代码
}
}
}
在外部类方法中,创建内部类对象,进行访问
class Party {//外部类,聚会
public void puffBall(){// 吹气球方法
class Ball {// 内部类,气球
public void puff(){
System.out.println("气球膨胀了");
}
}
//创建内部类对象,调用puff方法
new Ball().puff();
}
}
public static void main(String[] args) {
//创建外部类对象
Party p = new Party();
//调用外部类中的puffBall方法
p.puffBall();
}
/*
* 内部类
* 内部类分成员内部类和局部内部类
*/
public class Outer {
private String name = "外部名字";
/*
* 成员内部类
*/
class Inner{
public void method(){
System.out.println(name);
}
}
public void outMethod(){
/*
* 局部内部类
*/
class Inner2{
public void method(){
System.out.println(name);
}
}
//创建局部内部类对象
Inner2 in = new Inner2();
in.method();
}
}
/*
* 测试成员内部类创建对象
*/
public class Test {
public static void main(String[] args) {
//创建成员内部类对象
Outer.Inner in = new Outer().new Inner();
//调用成员内部类方法
in.method();
System.out.println("-----------");
Outer out = new Outer();
out.outMethod();
}
}
内部类是为了应对更为复杂的类间关系。我们在完成计算机语言相对底层的位置才会涉及,日常业务中很难遇到,这里不做赘述。
最常用到的内部类就是匿名内部类,是局部内部类的一种。
匿名内部类有两个步骤:
作用:匿名内部类是创建某个类型子类对象的快捷方式。
格式:
new 父类或接口(){
//进行方法重写
};
代码演示
//已经存在的父类:
public abstract class Person{
public abstract void eat();
}
//定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量
Person p = new Person(){
public void eat() {
System.out.println(“我吃了”);
}
};
//调用eat方法
p.eat();
使用匿名对象的方式,将定义子类与创建子类对象两个步骤由一个格式一次完成,。虽然是两个步骤,但是两个步骤是连在一起完成的。
匿名内部类如果不定义变量引用,则也是匿名对象。代码如下:
new Person(){
public void eat() {
System.out.println(“我吃了”);
}
}.eat();
/*
* 定义Fly接口
*/
public interface Fly {
public abstract void open();
public abstract void fly();
public abstract void close();
}
public class YanZi implements Fly{
@Override
public void open() {
System.out.println("张开小翅膀");
}
@Override
public void fly() {
System.out.println("能飞3000米高空");
}
@Override
public void close() {
System.out.println("关闭小翅膀,安全着陆");
}
//一个类中可以定义多个类,但只能有一个类public的
class Person(){
}
}
/*
* 匿名内部类
*
* new 父类/接口(){
* //重写需要重写的方法
* };
*/
public class Test {
public static void main(String[] args) {
Fly yz = new YanZi();
yz.open();
yz.fly();
yz.close();
new YanZi().open();
System.out.println("-------------------");
//实现类对象赋值给父接口
Fly fj = new Fly(){
@Override
public void open() {
System.out.println("不需要张开翅膀,一直都是张开状态");
}
@Override
public void fly() {
System.out.println("喷气式助力飞行!");
}
@Override
public void close() {
System.out.println("不需要关闭翅膀,得哪撞哪");
}
};
fj.open();
fj.fly();
fj.close();
System.out.println("-----------------------------");
//实现类对象直接以匿名对象的方式调用方法
new Fly() {
@Override
public void open() {
System.out.println("小翅膀");
}
@Override
public void fly() {
System.out.println("乱飞");
}
@Override
public void close() {
System.out.println("各种撞树");
}
}.fly();
}
}
java的包,其实就是我们电脑系统中的文件夹,包里存放的是程序生成的.class文件。
当.class文件很多的时候,通常我们会采用多个包进行存放管理他们,这种方式称为分包管理,分包管理是组织软件项目结构的基本方式。
在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界。
类中声明的包必须与实际class文件所在的文件夹情况相一致,即类声明在a包下,则生成的.class文件必须在a文件夹下,否则,程序运行时会找不到类。
通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接
类中包的声明格式:
package 包名.包名.包名…;
如:Java帮帮官网网址javahelp.com.cn那么网址反写就为cn.com.javahelp
package cn.com.javahelp; //包的声明,必须在有效代码的第一行
import java.util.Scanner;
import java.util.Random;
public class Demo {}
在访问类时,为了能够找到该类,必须使用含有包名的类全名(包名.类名)。
包名.包名….类名
如: java.util.Scanner
java.util.Random
cn.com.javahelp.Demo
带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名();
cn.com.javahelp.Demo d = new cn.com.javahelp.Demo();
当我们要使用一个类时,这个类与当前程序在同一个包中(即同一个文件夹中),或者这个类是java.lang包中的类时通常可以省略掉包名,直接使用该类。
如:cn.itcast包中有两个类,PersonTest类,与Person类。我们在PersonTest类中,访问Person类时,由于是同一个包下,访问时可以省略包名,即直接通过类名访问 Person。
类名 变量名 = new类名();
Person p = new Person();
package cn.com.javahelp02;
public class Person {}
/*
* 用public修饰的类 可以在其他包中访问 使用默认权限修饰的类 只能在本包下访问 其他包无法访问
*/
public class Tree {
public void chengLiang(){
System.out.println("可以乘凉");
}
}
import cn.com.javahelp.Person;
public class Test {
public static void main(String[] args) {
//在访问类时,为了能够找到该类,使用类时,应该使用 包含 包 的类全名。
// cn.itcast.Person p = new cn.itcast.Person();
// p.eat();
// cn.itcast6.Tree t = new cn.itcast6.Tree();
//当在同一个文件夹下不需要使用类全名
// Tree t = new Tree();
// t.chengLiang();
//因为String在java.lang包下 所以不需要使用类全名不需要导包
// String s = "";
//当被使用的类与使用的类不在同一个文件夹下时,
//被使用者必须用public修饰才可被其他包下的类访问,
//我们可以通过导包的方式使用该类,避免使用类全名
Person p = new Person();
//当多个文件夹下有相同的类名时,只能有一个导包使用,其他必须仍然书写全名。
cn.itcast2.Person p2 = new cn.itcast2.Person();
}
}
我们每次使用类时,都需要写很长的包名。很麻烦,我们可以通过import导包的方式来简化。
可以通过导包的方式使用该类,可以避免使用全类名编写(即,包类.类名)。
导包的格式:
import 包名.类名;
当程序导入指定的包后,使用类时,就可以简化了。演示如下
//导入包前的方式
//创建对象
java.util.Random r1 = new java.util.Random();
java.util.Random r2 = new java.util.Random();
java.util.Scanner sc1 = new java.util.Scanner(System.in);
java.util.Scanner sc2 = new java.util.Scanner(System.in);
//导入包后的方式
import java.util.Random;
import java.util.Scanner;
//创建对象
Random r1 = new Random();
Random r2 = new Random();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(System.in);
在Java中提供了四种访问权限,使用不同的访问权限时,被修饰的内容会有不同的访问权限,以下表来说明不同权限的访问能力:
public | protected受保护的 | default默认 | private私有的 | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
所以,在日常开发过程中,如果允许其他包的类访问使用public,如果仅允许其他包的子类访问使用protected,仅本包内的类访问使用默认,仅能本类中访问使用private。
/*
* 访问权限修饰符
*/
public class Person {
public void method1(){
System.out.println("公共方法");
}
protected void method2(){
System.out.println("受保护的方法(为了给子类使用)");
}
void method3(){
System.out.println("默认权限方法");
}
private void method4(){
System.out.println("私有方法");
}
//同一个类中
public void test(){
method1();
method2();
method3();
method4();
}
}
/*
*四种方法都可以执行
*/
public class Test {
public static void main(String[] args) {
Person p = new Person();
p.test();
}
}
/*
* 同一个包下 子类 或者无关类
*/
public class Student {
public void test(){
Person p = new Person();
p.method1();
p.method2();
p.method3();
// p.method4();
}
}
import cn.itcast7.Person;
/*
* 不同包下的子类
*/
public class Student extends Person{
public void test(){
method1();
method2();
// method3();
// method4();
}
}
import cn.com.javahelp.Person;
/*
* 不同包下的无关类
*/
public class Test {
public static void main(String[] args) {
Person p = new Person();
p.method1();
// p.method2();
// p.method3();
// p.method4();
}
}
归纳一下:在日常开发过程中,编写的类、方法、成员变量的访问
一个项目随着功能的增加、继承树的扩展会出现众多的Java类。这时,不仅需要将一个类中的整体功能抽取出成为独立方法,还需要将功能边界即功能所有者界定,而这就是定义类与包的过程。在定义好不同的类之后,再将相似功能的类放到同一个包中进行统一管理。
普通代码块就是直接定义在方法或语句中,以”{}”划定的代码区域,此时只需要关注作用域的不同即可,方法和类都是以代码块的方式划定边界的,如:
class Demo{
public static void main(String[] args) {
{
int x = 1;
System.out.println("普通代码块" + x);
}
int x = 99;
System.out.println("代码块之外" + x);
}
}
结果:
普通代码块1
代码块之外99
直接定义在类中成员位置的代码块,优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作,每创建一个对象均会执行一次构造代码块。
public class Person {
private String name;
private int age;
static{
System.out.println("静态代码块执行了");
}
{
System.out.println("构造代码块执行了");
}
Person(){
System.out.println("Person无参数的构造函数执行");
}
Person(int age){
this.age = age;
System.out.println("Person(age)参数的构造函数执行");
}
}
class PersonDemo{
public static void main(String[] args) {
Person p = new Person();
Person p1 = new Person(23);
}
}
静态代码块是定义在成员位置,使用static修饰的代码块。
特点:
public class Person {
private String name;
private int age;
//静态代码块
static{
System.out.println("静态代码块执行了");
}
}
public class Person {
private String name;
//成员代码块 又叫构造代码块 对象级别的代码块,每次创建对象都会执行一次
{
System.out.println("我是构造代码块");
}
//静态代码块 类级别的代码块,只有第一次创建对象时才运行,之后创建对象就不执行了
static{
System.out.println("静态代码块");
}
public Person(String name) {
super();
this.name = name;
}
public Person() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
int a = 10;
//局部代码块限定变量作用域范围
{
System.out.println(a);
int b = 20;
System.out.println(b);
}
// System.out.println(b);
Person p = new Person();
Person p2 = new Person("刘备");
Person p3 = new Person("刘备");
Person p4 = new Person("刘备");
Person p5 = new Person("刘备");
}
}
1) 使用匿名方法创建对象传参并调用show方法,并在控制台打印
interface Inter{void show();}class Outer{//补全代码}class OuterDemo{public static void main(String[] args) {Outer.method().show();//HelloWorld} |
---|
答案:public static Inter method(){
return new Inter(){
@Override
public void show() {
System.out.println("HelloWorld");
}
};
}
static 方法在内存中只有一份,普通方法在每个被调用中维持一份拷贝,static方法属于类方法随着类的加载而加载!普通方法属于对象随着对象的创建而存在随着对象的消失而消失。
答:static变量在java中是属于类的,它在所有实例中的值都是一样的。当java虚拟机载入的时候会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有和任何实例关联上。
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。
这四个作用域的可见范围如下表所示。
说明:如果在修饰的元素上面没有写任何访问修饰符,则表示空的(default)。
要求包含成员变量和成员方法,构造方法(无参,满参)
主方法中匿名调用该成员方法并打印
答案:
public class Person {private String name;private int age;private String addr;Person(){} Person(String name,int age,String addr){this.name = name;this.age = age;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}//成员show方法public void show(){System.out.println("该学生信息:\n"+"姓名:"+name+"\n年龄:"+age+"\n住址:"+addr);}}public class Test_3 {public static void main(String[] args) {new Person("张三",12,"昌平修正大厦六楼").show();}} |
---|
要求包含成员变量和成员方法,构造方法(无参,满参)
主方法中要求调用method(工人1,工人2)方法使用匿名对象传参;
method方法内要求计算两位工人当前月份的工资和,并返参打印
答案:
public class Worker{private String name;private int age;private double wage;private String month;public Worker(String name, int age, double wage, String month) {super();this.name = name;this.age = age;this.wage = wage;this.month = month;}public Worker() {super();}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getWage() {return wage;}public void setWage(double wage) {this.wage = wage;}public String getMonth() {return month;}public void setMonth(String month) {this.month = month;} } public static void main(String[] args) {double method = method(new Worker("zhangsan",12,1000,"12"),new Worker("zhangsan",12,1000,"12"));System.out.println(method);}public static double method(Worker w1,Worker w2){ return w1.getWage()+w2.getWage();} |
---|
答案:
public class Calculator {public static double addition(double d1, double d2){//加法return d1 + d2;}public static double subtraction(double d1, double d2){//减法return d1 - d2;}public static double multiply(double d1, double d2){//乘法return d1 * d2;} public static double division(double d1, double d2) {//除法if (d2 == 0) {System.out.println("对不起,除数不能为0");return -0;}return d1 / d2;}} |
---|
比如: 手机
属性: 品牌,颜色 --定义成私有的
方法: 电话 功能 ---定义成静态的
展示 展示手机的品牌和颜色
答案:
class Phone{private String brand;private String color;public Phone(){}public Phone(String brand,String color){this.brand = brand;this.color = color;}public static void call(){System.out.println("打电话");}public void show(){System.out.println("手机的品牌是:"+brand+",的颜色是:"+color);}}public class Test { public static void main(String[] args) { Phone.call();//静态方法可以直接通过类名调用 Phone p = new Phone("苹果","土豪粉"); p.show();//非静态方法只能对象调用 }} |
---|
在类中定义一个String类型的成员变量和一个String类型的静态变量;
然后在定义两个无返回值的方法,一个定义为成员方法,另一个定义为静态方法;
最后测试它们是否能使用成员变量和静态变量。
答案:
public class Hello {public String s1 = "aaa";public static String s2 = "bbb";public void method(){System.out.println(s1);System.out.println(s2);}public static void function(){System.out.println(s1);System.out.println(s2);}public static void main(String[] args) {new Hello().method();function();}}测试结果:在静态方法function() 中,无法访问非静态成员 s1。 |
---|