面向对象的三条主线
1.Java类及类的成员:属性、方法、构造器:代码块、内部类
2.面向对象的三大特征:封装、继承性、多态性
3.其他关键字:this、super、static、final、abstract、interface、package等
面向过程(POP)与面向对象(OOP)
两者都是一种思想
面向对象是相对于面向过程而言的。
面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
面向对象的三大特征:封装、继承、多态
eg:
人把大象装进冰箱
1.面向过程
①把冰箱打开
②抬起大象,塞进冰箱
③把冰箱门关闭
2.面向对象
人{
打开(冰箱){
冰箱.开开();
}
抬起(大象){
大象.进入(冰箱);
}
}
冰箱{
开开(){}
}
大象{
进入(冰箱){}
}
Java的基本元素:类和对象
类和对象是面向对象的核心概念:
类是对一类事物的描述,是抽象的、概念上的定义。常见的类的成员有:属性和方法
对象是实际存在的该类事物的每个个体,因而也称为实例。万事万物皆“对象”
面向对象程序设计的重点是类的设计
设计类,就是设计类的成员
二者的关系:对象,是由类new出来的,派生出来的。
类和对象的使用(面向对象思想落地的实现)
1.创建类,设计类的成员
2.创建类的对象
3.通过"对象.属性"或"对象.方法"调用对象的结构
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的)。意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值
public class PersonTest {
public static void main(String[] args) {
//创建Person类的对象
Person p1 = new Person();
//调用对象的结构:属性、方法
//调用属性:对象.属性
p1.name = "Tom";
p1.isMale = true;
System.out.println(p1.name);
//调用方法:对象.方法
p1.eat();
p1.talk("chinese");
Person p2 = new Person();
System.out.println(p2.name);//null
}
}
class Person {
//属性
String name;
int age = 1;
boolean isMale;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void talk(String language) {
System.out.println("这个人说的语言是" + language);
}
}
类的成员之一:属性
属性(成员变量)(非static)vs局部变量
相同点:
定义变量的格式:数据类型 变量名 = 变量值
先声明后使用
变量都有其对应的作用域
不同点:
在类中声明的位置不同
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
关于权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符
常见的权限修饰符:private、public、缺省、protected
局部变量不可以使用权限修饰符
默认初始化值的情况
属性:类的属性,根据其类型都有默认初始化值
整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0(或’\u0000’)
布尔型(boolean):false
引用数据类型(类、数组、接口):null
局部变量:没有默认初始化值。
意味着,我们在调用局部变量之前,一定要显式赋值。
特别的:形参在调用时,我们赋值即可。
在内存中加载的位置
属性:加载到堆空间中
局部变量:加载到栈空间中属性赋值:
/**
* 总结:属性赋值的先后顺序
*
* ① 默认初始化值
* ② 显示初始化值
* ③ 构造器中赋值
* ④ 通过“对象.方法”或“对象.属性”的方式,赋值
* ⑤ 在代码块中赋值
* 以上操作的先后顺序:① - ② / ⑤ - ③ - ④
*/
public class UserTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.age);
User u1 = new User(2);
u1.setAge(3);
System.out.println(u1.age);
}
}
class User {
String name;
int age = 1;
public User() {
}
public User(int a) {
age = a;
}
public void setAge(int a) {
age = a;
}
}
类的成员之二:方法
1. 方法的分类:按照是否有形参及返回值
无返回值有返回值无形参void 方法名(){}返回值的类型 方法名(){}有形参void 方法名(形参列表){}返回值的类型 方法名(形参列表){}
2. 方法的声明:权限修饰符 返回值类型 方法名(形参列表){方法体}
3. 说明:
3.1 关于权限修饰符
private、public、缺省、protected
3.2 返回值类型:有返回值VS没有返回值
如果方法有返回值,则必须在方法声明时,指定返回值类型。同时,方法中需要使用return关键字来返回指定类型的变量或常量
如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,不需要使用return。但是,如果使用的话,只能"return;"表示结束此方法的意思。
3.3 方法名:属于标识符,遵循标识符的规则和规范
3.4 形参列表:方法可以声明0个,1个,或多个形参
格式:数据类型1 形参1,数据类型2 形参2,…
3.5 方法体:方法功能的体现
4. 方法的使用中,可以调用当前类的属性或方法
特殊的:方法A又调用了方法A:递归方法
方法中不可以定义方法return关键字的使用
1.使用范围:使用在方法体中
2.作用:① 结束方法
②针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。
3 .注意点:return关键字后面不可以声明执行语句。
/**
* 对象数组
* 引用类型的变量,只可能存储两类值:null或地址值(含变量的类型)
*/
public class StudentTest {
public static void main(String[] args) {
Student[] student = new Student[10];
for (int i = 0; i < student.length; i++) {
student[i] = new Student();
student[i].number = i + 1;
student[i].state = 3;
student[i].score = (int) (Math.random() * (100 - 0 + 1));
}
//遍历数组
for (int i = 0; i < student.length; i++) {
System.out.print(student[i].number + "\t");
System.out.print(student[i].state + "\t");
System.out.print(student[i].score);
System.out.println();
}
}
}
class Student {
int number;
int state;
int score;
}
方法的重载
概念
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
特点
与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或者参数类型)。调用时,根据方法参数列表的不同来区别
示例
//返回两个整数的和
int add(int x,int y){return x+y;}
//返回三个整数的和
int add(int x,int y,int z){return x+y+z;}
//返回两个小数的和
double add(double x,double y){return x+y;}
在通过对象调用方法时,如何确定某一个指定的方法
方法名 —>参数列表
/**
* 方法的重载(overload)
*/
public class OverloadTest {
//以下的方法构成了重载
public void getSum(int i, int j) {
System.out.println(i + j);
}
public void getSum(double d1, double d2) {
System.out.println(d1 + d2);
}
public void getSum(String s, int i) {
System.out.println(s + i);
}
public void getSum(int i, String s) {
System.out.println(i + s);
}
}
可变形参的方法
允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
JDK5.0以前:采用数组形参来定义方法,传入多个同一类型的变量
public static void test(int a,String[] books);
JDK5.0:采用可变个数形参来定义方法,传入多个同一类型的变量
public static void test(int a ,String… books);
/**
* 可变个数形参的方法
* 具体使用
* 可变个数形参的格式:数据类型... 变量名
* 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个
* 可变个数形参方法与本类中方法名相同,形参不同的方法之间构成重载
* 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间,不构成重载。二者不能共存
* 可变个数形参在方法的形参中,必须声明在末尾
* 可变个数形参在方法的形参中,最多只能声明一个可变形参
*/
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(12);
test.show("Hello");
test.show("Hello", "World");
}
public void show(int i) {
System.out.println(i);
}
public void show(String s) {
System.out.println(s);
}
//以下两个方法是不可以并存的
public void show(String... strs) {
System.out.println(strs[0]);
System.out.println(strs[1]);
}
/*
public void show(String[] strs) {
System.out.println();
}
*/
//正确的
public void show(int i,String... strs) {
System.out.println(strs[0]);
System.out.println(strs[1]);
}
//错误的
/*
public void show(String... strs,int i) {
System.out.println(strs[0]);
System.out.println(strs[1]);
}
*/
}
方法参数的值传递机制
方法,必须由其所在类或对象调用才有意义,若方法含有参数;
形参:方法声明时的参数;方法定义时,声明的小括号内的参数
实参:方法调用时实际传给形参的参数值;方法调用时,实际传递给形参的数据
Java中方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
型参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
public class ValueTransferTest {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m=" + m + ",n=" + n);//m=10,n=20
ValueTransferTest test = new ValueTransferTest();
test.swap(m, n);
System.out.println("m=" + m + ",n=" + n);//m=10,n=20
Data data = new Data();
data.m = 15;
data.n = 25;
System.out.println("m=" + data.m + ",n=" + data.n);//m=15,n=25
ValueTransferTest test1 = new ValueTransferTest();
test1.swap1(data);
System.out.println("m=" + data.m + ",n=" + data.n);//m=25,n=15
}
public void swap(int m, int n) {
int temp = m;
m = n;
n = temp;
}
public void swap1(Data data) {
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data {
int m;
int n;
}
递归方法
递归方法:一个方法体内调用它自身
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无需循环控制
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
/**
* 递归方法
*/
public class RecursionTest {
public static void main(String[] args) {
//例1:计算1-100之间所有自然数的和
//方法一
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println(sum);
//方法二
RecursionTest recursionTest = new RecursionTest();
int sum1 = recursionTest.getSum(100);
System.out.println(sum1);
//例2:计算1-100之间所有自然数的乘积
//方法一
int multi = 1;
for (int i = 2; i <= 5; i++) {
multi *= i;
}
System.out.println(multi);
//方法二
RecursionTest recursionTest1 = new RecursionTest();
int multi1 = recursionTest1.getMulti(5);
System.out.println(multi1);
//例3:已知有一个数列:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。
RecursionTest recursionTest2 = new RecursionTest();
int f = recursionTest2.f(5);
System.out.println(f);
}
public int getSum(int n) {
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}
}
public int getMulti(int n) {
if (n == 1) {
return 1;
} else {
return n * getMulti(n - 1);
}
}
public int f(int n) {
if (n == 0) {
return 1;
} else if (n == 1) {
return 4;
} else {
return 2 * f(n - 1) + f(n - 2);
}
}
}
类的成员之三:构造器
/**
* 类的结构之三:构造器(或构造方法、constructor)的使用
* construct:建设、建造 constructor:建设者
*
* 一、构造器的作用
* 1.创建对象
* 2.给对象进行初始化
*
* 二、说明:
* 1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
* 2.定义构造器的格式:权限修饰符 类名(形参列表)
* 3.一个类中定义的多个构造器,彼此构成重载
* 4.一旦我们显示的定义了类的构造器之后,系统就不再提供默认的空参构造器。
* 5.一个类中,至少会有一个构造器。
* 三、特征:
* 1.它具有与类相同的名称
* 2.它不声明返回值类型。(与声明为void不同)
* 3.不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值
*
*/
public class PersonTest {
public static void main(String[] args) {
//创建类的对象:new + 构造器
Person p = new Person();
p.eat();
Person p1 = new Person("Tom");
System.out.println(p1.name);
//Person()....
//人吃饭
}
}
class Person {
//属性
String name;
int age;
//构造器
public Person() {
System.out.println("Person()....");
}
public Person(String n) {
name = n;
}
public Person(String n,int a ) {
name = n;
age = a;
}
//方法
public void eat() {
System.out.println("人吃饭");
}
}
类的成员之四:代码块(或初始化块)
package com.test.exer2;
/**
* 类的成员之四:代码块(或初始化块)
* 1.代码块的作用:用来初始化类、对象
* 2.代码块如果有修饰的话,只能使用static
* 3.分类:静态代码块 VS 非静态代码块
* 4.静态代码块
* 内部可以有输出语句
* 随着类的加载而执行,而且只执行一次
* 作用:初始化类的属性
* 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
* 静态代码块的执行,要优先于非静态代码块的执行
* 静态代码块内只能调用静态的属性、方法,不能调用非静态的结构
* 5.非静态代码块
* 内部可以有输出语句
* 随着对象的创建而执行
* 每创建一个对象,就执行一次非静态代码块
* 作用:可以在创建对象时,对对象的属性等进行初始化
* 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
* 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
* 对属性可以赋值的位置:
* ①默认初始化
* ②显式初始化
* ③构造器中初始化
* ④有了对象以后,可以通过“对象.属性”或“对象.方法”的方式,进行赋值
* ⑤在代码块中赋值
*/
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
Person person = new Person();
System.out.println(person.age);
System.out.println(desc);
}
}
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-2");
}
static {
desc = "中国人";
System.out.println("Hello,static block-1");
}
//非静态代码块
{
System.out.println("Hello,block");
//调用非静态结构
age = 1;
//调用静态结构
desc = "华人";
}
//方法
public void eat() {
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void info() {
System.out.println("快乐");
}
}
类的成员之五:内部类
当一个事物的内部,还有一部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类
package com.test.exer;
/**
* 类的内部成员之五:内部类
* 1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
* 3.成员内部类:
* 一方面,作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被四种不同的权限修饰
* 另一方面,作为一个类:
* >类内可以定义属性、方法、构造器等
* >可以被final修饰,表示此类不能被继承,言外之意,不使用final,就可以被继承
* >可以被abstract修饰
* 4.关注如下的3个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类中区分调用外部类的结构
* 4.3 开发中局部内部类的使用 见<InnerClassTest1.java>
* 总结:
* 成员内部类和局部内部类,在编译以后,都会生成字节码文件
* 格式:成员内部类:外部类$内部类名.class
* 局部内部类:外部类$数字 内部类名.class
*/
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
bird.display("乌鸦");
}
}
class Person {
String name = "小明";
int age;
public void eat(){
System.out.println("吃饭");
}
//静态成员内部类
static class Dog {
String name;
int age;
public void show(){
System.out.println("狗");
}
}
//非静态成员内部类
final class Bird {
String name = "鸟";
public void sing(){
System.out.println("我是一只鸟");
Person.this.eat();//调用外部类的非静态属性
}
public void display(String name){
System.out.println(name);//方法的形参 形参的name
System.out.println(this.name);//内部类的属性 Bird的name
System.out.println(Person.this.name);//外部类的属性 Person的name
}
}
public void method() {
/**
* 在局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量的话,要求此局部变量声明为final。
* jdk 7及之前版本:要求此局部变量显式的声明为final的
* jdk 8及之后的版本:可以省略final的声明
*/
//局部内部类
class AA {
}
}
{
//局部内部类
class BB {
}
}
public Person() {
//局部内部类
class CC {
}
}
}
package com.test.exer;
public class InnerClassTest1 {
//开发中很少见
public void methd(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
//return new MyComparable();
//方式二:
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
JavaBean
JavaBean是一种Java语言写成的可重用组件
所谓JavaBean,是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
/**
* JavaBean类
*/
public class Custome {
private int id;
private String name;
public Custome() {
}
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
对象
匿名对象
1.理解:我们创建的对象没有显式的赋给一个变量名。即为匿名对象
2.特征:匿名对象只能调用一次
3.使用
public class PhoneTest {
public static void main(String[] args) {
new Phone().price = 999;
new Phone().showPrice();//0.0
PhoneMall mall = new PhoneMall();
//匿名对象的使用
mall.show(new Phone());
}
}
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
class Phone {
double price;//价格
public void sendEmail() {
System.out.println("发送邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
public void showPrice() {
System.out.println("手机价格为"+price);
}
}
面向对象的特征之一:封装
我们程序设计追求“高内聚,低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
权限修饰符修饰符类内部同一个包不同包的子类同一个工程privateyes(缺省)yesyesprotectedyesyesyespublicyesyesyesyes
注意:不同包下的普通类(非子类),不可以调用private、缺省、protected权限的属性、方法。
/**
* 面向对象的特征之一:封装与隐藏
* 一、问题的引入
* 当我们创建一个类的对象以后,我们通过“对象.属性”的方式,对对象的属性进行赋值,这里,赋值操作要受到属性的数据类性和存储范围的制约。
* 除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。
* 这个条件就不能在属性声明时体现,我们只能通过方法限制条件的添加。
* 同时,我们需要避免用户再使用“对象.属性”的方式对属性进行赋值。则需要对属性声明为私有的(private)
* 二、封装性的体现
* 我们将类的属性私有化(private),同时,提供公共(public)的方法来获取(getXxx)和设置(setXxx)此属性的值
*
* 拓展:封装性的体现:① 如上 ② 不对外暴露的私有的方法 ③ 单例模式(将构造器私有化) ④ 如果不希望类在包外被调用,可以将类设置为缺省的 。。。
*
* 三、封装性的体现,需要权限修饰符来配合。
* 1.java规定的4种权限:(从小到大排列):private、缺省、protected、public
* 2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
* 3.具体的:4中权限都可以用来修饰的内部结构:属性、方法、构造器、内部类
* 修饰类的话,只能使用:缺省(default)、public
*
*/
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Animal();
a.name = "大黄";
a.age = 1;
// a.legs = 4;
a.eat();
a.show();
System.out.println(a.getLegs());
}
}
class Animal {
String name;
int age;
private int legs;//腿的个数
public void eat() {
System.out.println("动物进食");
}
public void show() {
System.out.println("name=" + name + ",age=" + age + ",legs=" + legs);
}
//属性的设置
public void setLegs(int l) {
if (l >= 0 && l % 2 == 0) {
legs = 1;
} else {
legs = 0;
}
}
//属性的获取
public int getLegs(){
return legs;
}
}
面向对象的特征之二:继承性
/**
* 面向对象的特征之二:继承性
* 一、继承性的好处
* ① 减少了代码的冗余,提高了代码的复用性
* ② 便于功能的扩展
* ③ 为之后多态性的使用,提供了前提
* 二、继承性的格式:class A extends B {}
* A:子类、派生类、subclass
* B:父类、超类、基类、superclass
* 2.1体现:
* 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
* 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
* 只是因为封装性的影响,使得子类不能直接调用父类的结构而已
* 2.2子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的扩展。
* 子类和父类的关系,不同于子集和集合的关系
* extends:延展、亏站
* 三、Java中关于继承性的规定: Java只支持单继承和多层继承,不允许多重继承
* ① 一个类可以被多个子类继承
* ② 一个类只能有一个父类
* ③ 子父类是相对的概念
* ④ 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
* ⑤ 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
* 四、1.如果我们没有显示的声明一个类的父类的话,则此类继承于java.lang.Object类
* 2.所有的Java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
* 3.意味着,所有的Java类具有java.lang.Object类声明的功能。
*/
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
Student s1 = new Student();
// s1.age = 12;
}
}
public class Person {
String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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 void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
public class Student extends Person {
// String name;
int age;
String major;
public Student() {
}
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
// public void eat() {
// System.out.println("吃饭");
// }
//
// public void sleep() {
// System.out.println("睡觉");
// }
public void study() {
System.out.println("学习");
}
}
方法的重写(override/overwrite)
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法
注意:
子类和父类中同名同参数的方法必须同时声明为非satic的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
/**
* 方法的重写(override/overwrite)
* 1.重写:子类继承父类以后,可以对父类中同名同参数的方法进行覆盖操作
* 2.应用:重写以后,当创建子类对象以后,通过子类对象调用父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
* 3.重写的规定:
* 方法的声明:权限修饰符 返回值类型 方法名(参数列表) throws 异常的类型{
* //方法体
* }
* 约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
* ① 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
* ② 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
* 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
* 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型只能是A类或A类的子类
* 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型只能是相同的基本数据类型(必须也是double)
* ③ 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
* 子类不能重写父类中声明为private权限的方法
* ④ 子类方法抛出的异常不能大于父类被重写方法的异常
*
*/
public class PersonTest1 {
public static void main(String[] args) {
Student1 s = new Student1("计算机");
s.eat();
s.walk(10);
s.study();
}
}
public class Student1 extends Person1 {
String major;
public Student1() {
}
public Student1(String major) {
this.major = major;
}
public void study() {
System.out.println("学习,专业是" + major);
}
//对父类中的eat方法进行了重写
public void eat() {
System.out.println("学生吃饭");
}
}
public class Person1 {
String name;
int age;
public Person1() {
}
public Person1(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void walk(int distance) {
System.out.println("走路,走的距离是" + distance + "公里");
}
}
子类对象实例化的全过程
子类对象实例化的全过程
- 从结果上来看,(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性- 从过程上来看
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止,正因为加载过所有的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
面向对象的特征之三:多态性
/**
* 面向对象特征之三:多态性
* 1.理解多态性:可以理解为一个事物的多种形态。
* 2.何为多态性:
* 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
* 3.多态的使用:
* 有了对象那个的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
* 总结:编译看左边,运行看右边。
* 4.多态性的使用前提:① 类的继承关系 ② 方法的重写
* 5.对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)
*
* 虚拟方法调用
* 正常的方法调用
* Person e = new Person();
* e.getInfo();
* Student e = new Student();
* e.getInfo();
* 虚拟方法调用(多态情况下)
* 子类中定义了与父类同名同参数的方法,在多态情况下,将此事父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
* Person e = new Student();
* e.getInfo();//调用Student类的getInfo()方法
* 编译时类型和运行时类型
* 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法 --动态绑定
*
*
*/
public class PersonTest3 {
public static void main(String[] args) {
Person3 p3 = new Person3();
p3.eat();
Man3 m3 = new Man3();
m3.eat();
m3.age = 25;
m3.earnMoney();
System.out.println("****************");
//对象的多态性:父类的引用指向子类的对象
Person3 p4 = new Man3();
Person3 p5 = new Woman();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法-----虚拟方法调用l
p4.eat();//男人,吃饭 ctrl进去是Person3类,不是Man3类
p4.walk();//男人,走路
//不能调用子类所特有的方法,编译时,p4是Person类型
//p4.earnMoney();//此方法调用不了,因为p4是Person3类型
//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
//如何才能调用子类特有的属性和方法?
//向下转型使用强制类型转化符。
Man3 m4 = (Man3) p4;
m4.earnMoney();
m4.isSmoking = true;
//使用强转时,可能出现ClassCastException的异常。
//Woman w1 = (Woman) p4;
//w1.goShopping();
/**
* instanceof关键字的使用
* a instanceof A:判断对象a是否是类A的实例,如果是,返回true,如果不是,返回false.
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,西安进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型
*
* 如果 a instanceof A 返回true,则a instanceof B 也返回true
* 其中,类B时类A的父类
*/
if(p4 instanceof Woman){
Woman w1 = (Woman) p4;
w1.goShopping();
System.out.println("****Woman***");
}
if(p4 instanceof Man3){
Man3 m5 = (Man3) p4;
m5.earnMoney();
System.out.println("****Man***");
}
if(p4 instanceof Person3){
System.out.println("****Person***");
}
if(p4 instanceof Object){
System.out.println("****Object***");
}
//练习:
//问题一:编译时通过,运行时不通过
Person3 p6 = new Woman();
Man3 m6 = (Man3) p6;//ClassCastException
//问题二:编译通过,运行时也通过
Object obj = new Woman();
Person3 p = (Person3) obj;
//问题三:编译不通过
//Man3 m = new Woman();
}
}
public class Person3 {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
public class Man3 extends Person3 {
boolean isSmoking;
public void earnMoney() {
System.out.println("男人,挣钱");
}
@Override
public void eat() {
System.out.println("男人,吃饭");
}
@Override
public void walk() {
System.out.println("男人,走路");
}
}
public class Woman extends Person3 {
boolean isBeauty;
public void goShopping() {
System.out.println("女人,购物");
}
public void eat() {
System.out.println("女人,吃饭");
}
public void walk() {
System.out.println("女人,走路");
}
}
问题
package com.test.exer;
/**
* 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法
* 系统将不可能把父类里的方法转移到子类中:编译看左边运行看右边
*
* 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的变量,
* 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
*/
class Base{
int count = 10;
public void display(){
System.out.println(this.count);
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;
//== 对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10 多态性不适用于属性
b.display();//20
}
}
练习
package com.test.exer;
public class InstanceTest {
public static void main(String[] args) {
InstanceTest test = new InstanceTest();
test.method(new Student());
}
public void method(Person e) {
//虚拟方法调用
String info = e.getInfo();
System.out.println(info);
//方式一
if (e instanceof Graduate) {
System.out.println("a Graduate");
System.out.println("a Student");
System.out.println("a Person");
} else if (e instanceof Student) {
System.out.println("b Student");
System.out.println("b Person");
} else {
System.out.println("c Person");
}
//方式二
if (e instanceof Graduate) {
System.out.println("a Graduate");
}
if (e instanceof Student) {
System.out.println("b Student");
}
if (e instanceof Person) {
System.out.println("c Person");
}
}
}
class Person {
protected String name = "Person";
protected int age = 50;
public String getInfo() {
return "name:" + name + "\n" + "age:" + age;
}
}
class Student extends Person {
protected String school = "pku";
public String getIfo() {
return "name:" + name + "\n" + "age:" + age + "\n" + "school:" + school;
}
}
class Graduate extends Student {
public String major = "IT";
public String getInfo() {
return "name:" + name + "\n" + "age:" + age + "\n" + "school:" + school + "\n" + "major:" + major;
}
}
抽象类和与抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用,类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的 实例,这样的类叫抽象类。
package com.test.exer2;
/**
* abstract 关键字的使用
* 1.abstract:抽象的
* 2.abstract可以用来修饰的结构:类、方法
* 3.abstract修饰类:抽象类
* >此类不可实例化
* >抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
* >开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
*
* 4.abstract修饰方法:抽象方法
* >抽象方法只有方法的声明,没有方法体
* >包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的
* >若子类重写了父类中的所有的抽象方法后,此子类方可实例化
* 若子类没有重写了父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
* 5.注意点
* 5.1 abstract不能用来修饰:属性、构造器等结构
* 5.2 abstract不能用来修饰:私有方法、静态方法、final的方法
* abstract能修饰哪些结构?修饰以后,有什么特点?
* abstract可以用来修饰 类、方法
* 特点:类不能实例化,提供子类来实例化
* 方法没有方法体,类必须是抽象类,抽象方法只定义了一种功能的标准,具体的执行需要子类去实现
*/
public class AbstractTest {
public static void main(String[] args) {
//一旦PersonA类抽象了,就不可实例化
//PersonA p1 = new PersonA();
//p1.eat();
StudentA s1 = new StudentA();
}
}
abstract class PersonA{
String name;
int age;
public PersonA() {
System.out.println("***");
}
public PersonA(String name, int age) {
this.name = name;
this.age = age;
}
//不是抽象方法
//public void eat(){
//System.out.println("吃饭");
//}
//抽象方法
public abstract void eat();
public void walk(){
System.out.println("走路");
}
}
class StudentA extends PersonA{
public StudentA(String name, int age) {
super(name, age);
}
public StudentA() {
}
@Override
public void eat() {
System.out.println("学生吃饭");
}
}
匿名类
package com.test;
/**
* 抽象类的匿名子类
*/
public class PersonTest {
public static void main(String[] args) {
method(new StudentA());//匿名对象
Worker worker = new Worker();
method1(worker);//非匿名的类,非匿名的类
method1(new Worker());//非匿名的类,匿名的对象
//创建了一匿名子类的对象:p
PersonA p =new PersonA() {
@Override
public void eat() {
System.out.println("吃东西");
}
};
method1(p);//吃东西
//创建匿名子类的匿名对象
method1(new PersonA() {
@Override
public void eat() {
System.out.println("吃吃吃,就知道吃!");
}
});
}
public static void method1(PersonA p){
p.eat();
}
public static void method(StudentA s){
}
}
class Worker extends PersonA{
@Override
public void eat() {
}
}
练习
public abstract class Employee {
private String name;
private int number;
private MyDate birthday;
public Employee() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
public Employee(String name, int number, MyDate birthday) {
this.name = name;
this.number = number;
this.birthday = birthday;
}
public abstract int earnings();
@Override
public String toString() {
return "name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString();
}
}
public class SalariedEmployee extends Employee {
private int monthlySalary;//工资
public SalariedEmployee(String name, int number, MyDate birthday, int monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}
public SalariedEmployee() {
}
public int getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(int monthlySalary) {
this.monthlySalary = monthlySalary;
}
@Override
public int earnings() {
return monthlySalary;
}
public String toString() {
return "SalariedEmployee[" + super.toString() + "]";
}
}
public class HourlyEmployee extends Employee {
private int wage;//每小时的工资
private int hour;//月工作的小时数
public HourlyEmployee(String name, int number, MyDate birthday,int wage,int hour) {
super(name, number, birthday);
this.hour = hour;
this.wage = wage;
}
public HourlyEmployee() {
}
@Override
public int earnings() {
return wage * hour;
}
public String toString() {
return "HourlyEmployee[" + super.toString() + "]";
}
}
public class MyDate {
private int year;
private int month;
private int day;
public MyDate() {
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public String toDateString() {
return year + "年" + month + "月" + day + "日";
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
import java.util.Calendar;
import java.util.Scanner;
public class PayrollSystem {
public static void main(String[] args) {
//方式一:通过键盘输入
Scanner scanner = new Scanner(System.in);
System.out.println("输入当月月份:");
int month = scanner.nextInt();
//方式二:获取当前日历的月份,通过方法输入
Calendar instance = Calendar.getInstance();
int month1 = instance.get(Calendar.MONTH);
//new了一个数组,并不是new了一个对象,每个位置声明为employee类型,下边再new对象不可以再new employee
Employee[] employees = new Employee[2];
//Employee emp = new Employee();//抽象类不可以实例化
employees[0] = new SalariedEmployee("张三", 1001, new MyDate(1996, 9, 9), 10000);
employees[1] = new HourlyEmployee("李四", 1002, new MyDate(1994, 9, 9), 1231, 41);
for (int i = 0; i < employees.length; i++) {
System.out.println(employees[i]);
System.out.println(employees[i].earnings());
if ( month == employees[i].getBirthday().getMonth()) {
System.out.println("生日快乐");
}
}
}
}
多态的应用:模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上保留抽象类的行为方式。
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
package com.test;
/**
* 抽象类的应用:模板方法的设计模式
*/
public class TemplateTest {
public static void main(String[] args) {
Template t = new SubTemplate();
t.spendTime();
}
}
abstract class Template {
//计算某段代码执行所需要花费的时间
public void spendTime() {
long start = System.currentTimeMillis();
this.code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template {
@Override
public void code() {
boolean flag = true;
for (int i = 2; i <= 1000; i++) {
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
flag = false;
break;
}
if (false) {
System.out.println(i);
}
}
}
}
}
接口
概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头等都支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中"如果你是/要…则必须能…"的思想。继承是一个"是不是"的关系,而接口实现规则是"能不能"的关系。
接口本质是契约,标准,规范。
package com.test.exer3;
/**
* 接口的使用
* 1.接口使用interface来定义
* 2.Java中,接口和类是并列的两个结构
* 3.如何定义接口:定义接口中的成员
* 3.1 JDK7及之前:只能定义全局常量和抽象方法
* >全局常量:public static final 的,但是书写时,可以省略不屑
* >抽象方法:public abstract
* 3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
* 4.接口中不能定义构造器的!意味着接口不能实例化
* 5.Java开发中,接口通过让类去实现(implements)的方式来使用,
* 如果实现类覆盖了接口中的所有抽象方法,则此实现类可以实例化
* 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
* 6.Java类可以实现多个接口 --> 弥补了Java单继承性的局限性
* 格式:class AA extends BB implements CC,DD,EE{}
* 7.接口与接口之间可以继承,而且可以多继承
* 8.接口的具体使用,体现多态性
* 9.接口,实际上可以看做是一种规范
*
* 面试题:抽象类与接口有哪些异同?
*/
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
Plane plane = new Plane();
plane.fly();
}
}
interface Flyable {
//全局常量
public static final int MAX_SPEED = 7900;//第一宇宙速度
int MIN_SPEED = 1;//省略了public static final
//抽象方法
public abstract void fly();
void stop();//省略了public abstract
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("飞");
}
@Override
public void stop() {
System.out.println("停");
}
}
abstract class Kite implements Flyable{
@Override
public void fly() {
}
}
class Bullet extends Object implements Flyable,Attackable,CC{
@Override
public void fly() {
System.out.println();
}
@Override
public void stop() {
System.out.println();
}
@Override
public void attack() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
package com.test.exer3;
public class SubClassTest {
public static void main(String[] args) {
SubClass subClass = new SubClass();
//知识点1:接口中定义的静态方法,只能通过接口来调用
//subClass.method1();
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
subClass.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法。
//那么子类再没有重写此方法的情况中,默认调用的是父类中的同名同参数的方法-->类优先原则
//知识点4:如果子类(或实现类)实现了多个接口,而这多个接口中定义了同名同参数的默认方法
//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
subClass.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
@Override
public void method2() {
System.out.println("SubClass:上海");
}
@Override
public void method3() {
System.out.println("SubClass:深圳");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
}
package com.test.exer3;
public class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
package com.test.exer3;
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
//默认方法
default void method3(){
System.out.println("CompareA:上海");
}
}
package com.test.exer3;
public interface CompareB {
default void method3(){
System.out.println("CompareB:上海");
}
}
接口的应用:代理模式
应用场景:
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
分类:
静态代理(静态定义代理类)
动态代理(动态生成代理类)
JDK自带的动态代理,需要反射等知识
package com.test.exer3;
/**
* 接口的应用:代理模式
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork {
public void browse();
}
//被代理类
class Server implements NetWork {
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork {
private NetWork netWork;
public ProxyServer(NetWork netWork){
this.netWork = netWork;
}
public void check() {
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
netWork.browse();
}
}
接口的使用
package com.test.exer3;
/**
* 接口的使用
* 1.接口使用上也满足多态性
* 2.接口:实际上就是定义了一种规范
* 3.开发中,体会面向接口编程
*/
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2.创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3.创建了接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4.创建了接口的匿名实现类的匿名对象
com.transferData(new USB() {
@Override
public void start() {
System.out.println("MP3开始工作");
}
@Override
public void stop() {
System.out.println("MP3结束工作");
}
});
}
}
class Computer {
public void transferData(USB usb) {
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB {
//定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB {
@Override
public void start() {
System.out.println("U盘开始工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB {
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
关键字:this的使用
/**
* this关键字的使用
* 1.this可以用来修饰、调用:属性、方法、构造器
* 2.this修饰属性和方法
* this理解为:当前对象或当前正在创建的对象。
*
* 2.1在类的方法中,我们可以使用”this.属性“或”this.方法“的方式,调用当前对象属性或方法。
* 但是通常情况下,我们都选择省略”this.“。
* 特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用”this.变量“的方式,表明此变量是属性,而非形参
* 2.2在类的构造器中,我们可以使用”this.属性“或”this.方法“的方式,调用当前正在创建的对象属性或方法。
* 但是通常情况下,我们都选择省略”this.“。
* 特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用”this.变量“的方式,表明此变量是属性,而非形参
* 3.this来修饰或调用构造器
* ①我们在类的构造器中,可以显示的使用"this(形参列表)"方法,调用本类中指定的其他构造器。
* ② 构造器中不能通过"this(形参列表)"方式调用自己
* ③ 如果一个类中有n个构造器,则最多有n-1个构造器中使用了"this(形参列表)"
* ④ 规定:"this(形参列表)"必须声明在当前构造器的首行
* ⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其它的构造器
*/
public class PersonThisTest {
public static void main(String[] args) {
Personl p = new Personl("Tom",20);
System.out.println(p.getName()+p.getAge());
/*结果:
吃饭
20行代码
Tom20
*/
}
}
class Personl {
private String name;
private int age;
public Personl() {
this.eat();
String info = "20行代码";
System.out.println(info);
}
public Personl(String name) {
this();
this.name = name;
}
public Personl(int age) {
this();//该this调用了无参构造器
this.age = age;
}
public Personl(String name, int age) {
this(name);//该this调用了参数为name的构造器
this.age = age;
//this.name = name;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void eat() {
System.out.println("吃饭");
}
}
关键字:package、import的使用
package的使用
JDK中主要的包介绍
1.Java.lang–包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能
2. java.net–包含执行与网络相关的操作的类和接口。
3. lava.lo–包含能提供多种输入/输出功能的类。
4. java.util-- 包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
5. java.text–包含了一些java格式化相关的类
6. java.sql-包含了java进行JDBC数据库編程的相关类/接口
7.java.awt–包含了构成抽象窗口工具集 (abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。B/S C/S
package com.pacific;
//import java.util.Arrays;
//import java.util.Scanner;
import java.util.*;
import static java.lang.System.*;
/**
* 一、package关键字的使用
* 1.为了更好的实现项目中类的管理,提供包的概念
* 2.使用package声明类或接口所属的包,声明在源文件的首行
* 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
* 4.每"."一次代表一层文件目录
*
* 补充:同一个包下,不能命名同名的接口、类
* 不同的包下,可以命名同名的接口、类
*
* 二、import关键字的使用
* import:导入
* 1。在源文件中显式的使用import导入指定包下的类、接口
* 2.声明在包的声明和类的声明之间
* 3.如果需要导入多个接口,则并列写出即可
* 4.可以使用“xxx.*”的方式,表示可以导入xxx包下的所有结构
* 5.如果使用的类或接口是java.lang包下定义的,则可以省略import结构
* 6.如果使用的类或接口是本包下定义的,则可以省略import结构
* 7.如果在源文件中使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显式。如(xxx.yyy.zzz.类名)
* 8.如果使用“xxx.*”方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显式导入
* 9.import static:导入指定类或接口中的静态结构:属性或方法
*/
public class PackageTest {
public static void main(String[] args) {
String s = Arrays.toString(new int[]{1, 2, 3});
Scanner scanner = new Scanner(System.in);
Date date = new Date();
//全类名
java.sql.Date date1 = new java.sql.Date(231451234141L);
out.println("***");//因为import static java.lang.System.*; 所以可以直接out
}
}
关键字:super
/**
* super关键字的使用
* 1.super理解为,父类的
* 2.super可以用来调用:属性、方法、构造器
* 3.super的使用:调用属性和方法
* 3.1 我们可以在子类的方法或构造器中,通过使用”super.属性"或“super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super.“
* 3.2 特殊情况,当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中的属性。
* 3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.属性"的方式,表明调用的是父类中被重写的方法。
* 4.super调用构造器
* 4.1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
* 4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行
* 4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
* 4.4 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器(super();)
* 4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
*/
public class SuperTest {
public static void main(String[] args) {
Student2 s = new Student2();
s.show();
s.study();
}
}
public class Student2 extends Person2 {
String major;
int id = 1001;
public Student2() {
//super();//不管写不写,默认调用的是super()
}
public Student2(String major) {
//super();//不管写不写,默认调用的是super()
this.major = major;
}
public Student2(String name, int age, String major) {
super(name, age);//此时调用的是父类含有name和age参数的构造器
this.major = major;
}
@Override
public void eat() {
System.out.println("学生,吃饭");
}
public void study() {
System.out.println("学生,学习");
eat();//学生,吃饭 此方法等价于 this.eat()
super.eat();//人,吃饭
}
public void show() {
System.out.println("name=" + this.name + ",age=" + super.age);//先找当前类的属性,如果找不到再去父类找
System.out.println("id=" + this.id);//1001
System.out.println("id=" + super.id);//0
}
}
public class Person2 {
String name;
int age;
int id;
public Person2() {
}
public Person2(String name) {
this.name = name;
}
public Person2(String name, int age) {
this(name);
this.age = age;
}
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
关键字:instanceof
instanceof关键字的使用
a instanceof A:判断对象a是否是类A的实例,如果是,返回true,如果不是,返回false.
使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,西安进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型
如果 a instanceof A 返回true,则a instanceof B 也返回true
其中,类B时类A的父类
//使用强转时,可能出现ClassCastException的异常。
//Woman w1 = (Woman) p4;
//w1.goShopping();
/**
* instanceof关键字的使用
* a instanceof A:判断对象a是否是类A的实例,如果是,返回true,如果不是,返回false.
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,西安进行instanceof的判断,一旦返回true,就进行向下转型,如果返回false,不进行向下转型
*
* 如果 a instanceof A 返回true,则a instanceof B 也返回true
* 其中,类B时类A的父类
*/
if(p4 instanceof Woman){
Woman w1 = (Woman) p4;
w1.goShopping();
System.out.println("****Woman***");
}
if(p4 instanceof Man3){
Man3 m5 = (Man3) p4;
m5.earnMoney();
System.out.println("****Man***");
}
if(p4 instanceof Person3){
System.out.println("****Person***");
}
if(p4 instanceof Object){
System.out.println("****Object***");
}
//练习:
//问题一:编译时通过,运行时不通过
Person3 p6 = new Woman();
Man3 m6 = (Man3) p6;//ClassCastException
//问题二:编译通过,运行时也通过
Object obj = new Woman();
Person3 p = (Person3) obj;
//问题三:编译不通过
//Man3 m = new Woman();
关键字:static
package com.test.exer2;
/**
* static 关键字的使用
*
* 1.static:静态的
* 2.static可以用来修饰:属性、方法、代码块、内部类
* 3.使用static来修饰属性:静态变量(类变量)
* 3.1 属性,按是否使用static修饰,又分为,静态属性 VS 非静态属性(实例变量)
* 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改
* 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的
* 3.2 static修饰属性的其他说明
* ① 静态变量随着类的加载而加载。可以通过“类.静态变量”的方式进行调用
* ② 静态变量的加载要早于对象的创建。
* ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
* ④ 类变量 实例变量
* 类 yes no
* 对象 yes yes
* 3.3 静态属性举例:System.out; Math.PI
* 4.使用static来修饰方法 静态方法
* ① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
* ② 静态方法 非静态方法
* 类 yes no
* 对象 yes yes
* ③ 静态方法中,只能调用静态的方法或属性
* ④ 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
* 5.static注意点:
* 在静态的方法内,不能使用this关键字、super关键字
* 6.开发中,如何确定一个属性是否要声明为static的?
* 属性是可以被多个对象所共享的,不会随着对象的不同而不同的
* 类中的常量也常常声明为static
* 开发中,如何确定一个方法是狗咬声明为static的?
* 操作静态属性的方法,通常设置为static的
* 工具类中的方法,习惯上声明为static的 比如:Math、Arrays、Collections
*/
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.nation = "CHN";
System.out.println(c1.nation);//CHN
Chinese c2 = new Chinese();
System.out.println(c2.nation);//CHN
c2.nation = "SHANGHAI";
System.out.println(c1.nation);//SHANGHAI
System.out.println(c2.nation);//SHANGHAI
Chinese.nation = "中国";
c1.eat();
c1.show();
Chinese.show();
}
}
class Chinese {
String name;
int age;
static String nation;
public void eat(){
System.out.println("中餐");
}
public static void show(){
System.out.println("中国人");
//eat();//静态方法中,只能调用静态的方法或属性
}
}
类变量 VS 实例变量内存解析
关键字:final
package com.test.exer2;
/**
* final:最终的
* 1.final可以用来下修饰的结构:类、方法、变量
* 2.final用来修饰一个类:此类不能被其他类继承
* 比如:String类、System类、StringBuffer类
* 3.final用来修饰方法:表明此方法不可以被重写
* 比如:Object类中getClass();
* 4.final用来修饰变量,此时的”变量“就称为一个常量
* 4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
* 4.2 final修饰局部变量
* 尤其是使用final修饰形参时,表示此形参是一个常量,当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能重新赋值
* static 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;
}
public void show(final int num){
//num = 20;//编译不通过
}
public static void main(String[] args) {
FinalTest test = new FinalTest();
test.show(10);
}
}
final class FinalA {
}
设计模式与单例设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。式就像是经典的棋谱,不同的棋局,我们不同的棋谱,“套路”
所谓类的单例模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以指向类内部产生的该类对象的变量也必须定义成静态的。
单例模式的优点:
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源是,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式类解决。举例:java.lang.Runtime
应用场景:
网站的计数器
应用程序的日志应用
数据库连接池:数据库连接是一种数据库资源
配置文件的类
Application也是单例的典型应用
Windows的Task Manager(任务管理器)
Windows的Recycle Bin(回收站)
package com.test.exer2;
/**
* 单例设计模式
* 1.所谓类的单例模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
* 2.如何实现?
* 饿汉式 VS 懒汉式
* 3.区分饿汉式和懒汉式
* 饿汉式:
* 坏处:对象加载时间过长
* 好处:饿汉式是线程安全的
* 懒汉式:
* 好处:延迟对象的创建
* 坏处:目前的写法,线程不安全 -->多线程修改
*/
public class SingletomTest {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
System.out.println(order1 == order2);
}
}
//饿汉式
class Bank {
//1.私有化类的构造器
private Bank() {
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance() {
return instance;
}
}
//懒汉式
class Order {
//1.私有化类的构造器
private Order() {
}
//2.声明当前类对象,未初始化
//4.要求此对象也必须声明为静态的
private static Order instance = null;
//3.声明公共的静态的方法,返回类的对象的方法
public static Order getInstance() {
if (instance == null) {
instance = new Order();
}
return instance;
}
}
Main()方法的语法
public static void main(String[] args){//方法体}
权限修饰符:private 缺省 protected public —>封装性
修饰符:static\final\abstract\native 可以用来修饰方法
返回值类型:无返回值/有返回值—>return
方法名:需要满足标识符命名的规则、规范:“见名知意”
形参列表:重载 VS 重写;参数的值传递机制;体现对象的多态性
package com.test.exer2;
/**
* main()方法的使用说明:
* 1.main()方法作为程序的入口
* 2.main()方法也是一个普通的静态方法
* 3.main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
*/
public class MainTest {
public static void main(String[] args) {
//Main.main(new String[100]);
for (int i = 0; i < args.length; i++) {
System.out.println("***" + args[i]);
}
}
}
class Main {
public static void main(String[] args) {
args = new String[100];
for (int i = 0; i < args.length; i++) {
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
方法的重载和重写
1.二者的概念:
重载:我们在同一个类中,可以声明多个方法名相同形参列表不同的方法,彼此之间构成了重载,构造器也可以重载。
重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
2.重载和重写的规则:
重载:两同一不同
重写:
3.重载:不表现为多态性
重写:表现为多态性
MVC设计模式
MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,与数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
模型层
数据对象封装 mode.bean/domain
数据库操作类 model.dao
数据库 model.db
控制层 controller 处理业务逻辑
应用界面相关 controller.activity
存放fragment controller.fragment
显示列表的适配器 controller adapter
服务相关的 controller.service
抽取的基类 controller.base
视图层 view 显示数据
相关工具类 view.utils
自定义view view.ui