https://www.jianshu.com/p/7a5b0043b035
面向对象的思想概述
一、设计类、其实就是设计类的成员
二、类和对象的使用
三、如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着: 如果我们修改了一个对象的属性a,则不会影响另外一个一个对象属性a的值。
# 例子
public class PersionTest {
public static void main(String[] args) {
//创建对象
Persion persion = new Persion();
//调用属性 对象.属性
persion.name = "张三";
persion.age = 18;
System.out.println("姓名: " + persion.name + "年龄: " + persion.age);
//调用方法 对象.方法
persion.eat();
persion.speak("中国话");
//创建第二个对象
Persion persion2 = new Persion();
System.out.println(persion2.name);//因为persion2对象并没有赋值所以是null
//创建第三个对象
Persion persion3 = persion1;
System.out.println(persion3.name);//将p1变量保存的对象地址值赋值给p3,导致p1和p3指向了对空间中的同一个对象 张三
persion3.name = "王五";
System.out.println(persion1.name); //p3里面改的值,p1里面也改了
}
}
class Persion{
//属性
String name;
int age;
//方法
public void eat(){
System.out.println("我正在吃饭");
}
public void speak(String language){
System.out.println("我讲的是" + language);
}
}
四、对象的内存解析
根据上面的例子画图
p3已经不算是创建一个新的对象,相当于创建一个变量,p1赋值给了p3。两者都是使用同一个堆中的值。
属性(成员变量) vs 局部变量
相同点:
不同点:
在类中声明的位置不同
class user{
int age;
}
关于权限修饰符的不同
默认初始化的情况
属性: 类的属性,根据其类型,都有默认初始化值
整型(byte、short、int、long): 0
浮点型(float、double): 0.0
字符型(char): 0 或 ‘\u0000’
布尔型(boolean): false
引用类型: null
局部变量: 没有默认初始化值,意味着,我们在调用局部变量之前,一定要显示赋值。特别的: 形参在调用时,我们赋值即可。
在内存中加载的位置
方法: 描述类应该具有的功能。
自己定义的方法:
class User{
public void eat(){
System.out.println("我在吃饭");
}
public String getName(String name){
return name;
}
}
方法的声明
权限修饰符 返回值类型 方法名(形参列表){ //形参列表根据情况来定有还是没有
}
注意: static、final、abstract 来修饰的方法,后面再讲。
方法的说明
关于权限修饰符:
返回值类型: 有返回值 vs 无返回值
方法名: 属于标识符,遵循标识符的规则和规范, 起的名字尽量"见名知意“
形参列表: 可以声明0个, 1个,或者多个形参
方法体:
return关键字的使用:
方法的使用
public class Dict {
public static void main(String[] args) {
new DictTest().eat();//没有赋值一个变量名直接调用就是匿名对象
}
}
class DictTest{
public void eat(){
System.out.println("我正在吃饭");
}
定义
如果有两个方法的方法名相同,但参数不一致,那么可以说一个方法是另一个方法的重载。
具体说明如下:
“两同一不同”:
public class Load {
public void getSum(int a){
}
public void getSum(int a,int b){
}
public void getSum(String a){
}
public void getSum(String a,Double c){
}
}
判断是否是重载
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
在通过对象调用方式时,如何确定某一个指定的方法
方法名 —> 参数列表
public class Load {
public static void main(String[] args) {
New a = new New();
a.getName("张三");
}
}
class New{
// public void getName(String name){
//
// }
public void getName(String ... name){
//需要遍历不遍历输出的值是地址值
for (int i=0; i<name.length; i++){
System.out.println(name[i]);
}
}
// public void getName(String[] name){
// 相当于上面,二者相同
// }
//可变性参数只能写在末尾
public void getName(int age,String ... name){
}
//可变个数形参在方法的形参中,最多只能声明一个可变形参。
//多了否则会报错
// public void getName(String ... name,int ... age){
//
// }
}
封装概述
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式,这里就会用到一个关键字是private,在后面的使用成员变量时候,不能直接调动,只能通过方法调用;
封装的优点
封装性的体现
一、我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值。
二、不对外暴露的私有的方法
三、单例模式(将构造器私有化)
四、如果不希望类在包外被调用,可以将类设置为缺省的。
一个标准案例的使用
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
animal.setName("大黄");
System.out.println(animal.getName());
}
}
class Animal{
private String name;
private int age;
private int leg;//腿
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 int getLeg() {
return leg;
}
public void setLeg(int leg) {
this.leg = leg;
}
}
封装性体现,需要权限修饰符来配合
2.四种权限可以用来修饰类及类的内部结构: 属性、方法、构造器、内部类
3.具体的,4种权限都可以用来修饰类的内部结构: 属性、方法、构造器、内部类
3.1 修饰类的话,只能使用: 缺省、public 例如 class Dog{} public class Cat{}
总结封装性: Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在调用时的可见性的大小。
构造器的作用
# 创建类的对象: new + 构造器
Persion p = new Persion()
说明
# 修饰符公共的 有参构造器
public Persion(int age) {
this.age = age;
}
3.一个类中定义的多个构造器,彼此构成重载。
4.一旦我们定义了类的构造器之后,系统就不再提供默认的无参构造器
5.一个类中,至少会有一个构造器。可能是自己定义的,也可能是系统提供的。
public class Persion {
private String name;
public Persion() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
this关键字的使用
this修饰属性和方法
this理解为: 当前对象
在类的方法或者构造器中,我们可以使用"this.属性" 或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法或构造器的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表名此变量是属性,而非形参,例如:
# this指向的是属性age而非形参里面的age
public class Persion {
private String name;
public Persion(String name) {
this.name = name;
}
}
this调用构造器
package关键字的使用
补充: 同一个包下,不能命名同名的接口、类。
import关键字的使用
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
一、在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
public class 父类 {
}
public class 子类 extends 父类 {
}
体现: 一旦子类继承父类之后,子类就获取了父类中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
注意: 父类中私有的方法不可以直接调用了,只能在父类中定义一个公共的方法,内部调用私有方法,例如:
public void study(){
System.out.println("正在学习");
eat();
}
private void eat(){
System.out.println("正在吃饭");
}
二、子类继承父类以后,还可以声明自己特有的属性或方法: 实现功能的扩展
# 在继承父类的基础上扩展功能
public class Dog extends Animal {
public int age;
public void sleep(){
System.out.println("我在睡觉");
}
}
三、需要注意的是Java不支持多继承,但支持多重继承
四、
定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的 重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写),因为static方法是属于类的,子类无法覆盖父类中的方法。
应用
1.重写以后,当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法时,实际执行的是子类重写父类的方法。
public class father {
public void eat(){
System.out.println("我在吃饭");
}
}
public class son extends father {
public void eat(){
System.out.println("儿子在吃饭");
}
}
public class familyTest {
public static void main(String[] args) {
son son = new son();
son.eat(); //调用的是子类重写过后的方法 儿子在吃饭
}
}
super关键字的使用
代码案例:
class Animal{
String name = "动物";
int age;
int id = 1001;
public void eat(){
System.out.println("动物们在吃饭");
}
}
class Dog extends Animal{
String sex;
int id = 1002;
public void show(){
System.out.println(name);//因为子类和父类之间并没有同名的name属性,所以调用name可以省略super
System.out.println(super.id);//通过super获取父类的id
}
public void eat(){
super.eat();//调用父类中的eat方法
//System.out.println("狗在吃饭");
}
}
public class SuperTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.show();//动物 1001
dog.eat();// 动物们在吃饭
}
}
4.super调用构造器
super调用构造器理解: https://blog.csdn.net/qq_34115899/article/details/80325079
理解多态性: 可以理解为一个事物的多种形态。
何为多态性:
对象的多态性: 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
# 例子 Persion是父类,Man是子类。
Persion s1 = new Man();
多态的使用: 虚拟方法调用
对象的多态性,只适用于方法,不适用于属性。
# 示例
class Persion{
int id = 1001;
}
class Man extends Persion{
int id = 1002;
}
public class PersionTest {
public static void main(String[] args) {
Persion p = new Man();
System.out.println(p.id); //父类的1001
}
}
使用情景: 为了避免在向下转型时出现ClassCastException异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
public class PersionTest {
public static void main(String[] args) {
Persion p = new Man();
Persion p1 = new Woman();
// p.play();
//想使用子类里面的其它方法需要向下转型,使用instanceof进行判断
//p1 new的对象是Woman,也只能调用Woman里面的方法
//注意: 不能用p1 new Woman对象,来判断向下转型Man
if (p1 instanceof Woman){
Woman woman = (Woman)p1;
woman.eat();
}
if (p instanceof Man){
Man man = (Man)p; //向下转型
man.eat();
}
}
}
Object类是所有Java类的根父类
如果在类的声明中未使用extedns关键字指明其父类,则默认父类为java.lang.Object类
public class Persion{
}
//等价于
public class Persion extends Object{
}
是一个方法,而非运算符
只能适用于引用数据类型
Object类中equals()的定义
public boolean equals(Object obj){
return(this == obj);
}
说明: Object类中定义的equals()的作用是,比较两个对象的地址值是否相同。和==相同。
像String、Date、File、包装类等都重写了Object类中equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写。
class Man{
String name;
int age;
public Man(String name, int age) {
this.name = name;
this.age = age;
}
//手动重写equals (开发建议使用开发工具自动生成的equals)
@Override
public boolean equals(Object o) {
if (this == o){
return true;
}
if (o instanceof Man){
Man man = (Man)o;
return this.age == man.age && this.name.equals(man.name);
}else {
return false;
}
}
}
public class PersionTest {
public static void main(String[] args) {
Man man = new Man("王五",20);
Man man1 = new Man("王五",20);
System.out.println(man.equals(man1));
}
}
6.重写equals(方法的原则
当我们输出一个对象的引用时,实际上就是调用当前对象的toString( )
Object类中的toString()的定义
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
像String、Date、File、包装类都重写了Object类中的toString()方法,使得在调用对象的 toString()时,返回"实体内容"信息。
自定义类重写toString()方法
class Man{
String name;
int age;
@Override
public String toString() {
return "Man{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class PersionTest {
public static void main(String[] args) {
Man man = new Man();'
//调用的就是重写之后的toString()方法而不是Object里面的toString()方法了
System.out.println(man.toString());
}
}
步骤
导入Junit4包 (推荐Maven导入方式)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
创建Java类,进行单元测试。
此类中声明单元测试方法
此单元测试方法上需要声明注释 @Test,并在单元测试类中导入包: import org.junit.Test;
package Excel;
import org.junit.Test;
public class PersionTest {
@Test
public void test(){
int a = 0;
System.out.println(a);
}
}
好处
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
包装类的使用:
public class WapperTest {
//基本数据类型 -- >包装类;调用包装类的构造器
@Test
public void test1(){
int num = 10;
int num2 = 10;
Integer integer = new Integer(num);
Integer integer1 = new Integer(num2);
System.out.println(integer.toString());
System.out.println(integer.equals(integer));//包装了重写equals方法比较的是内容了
}
//包装类 --->基本数据类型:调用包装类的xxxValue()
@Test
public void test2(){
Integer integer = new Integer(1);
int num = integer.intValue();
System.out.println(integer);
}
//JDK 5.0新特性:
@Test
public void test3(){
//自动装箱
int num = 10;
Integer integer = num;
System.out.println(integer);
//自动拆箱
Integer integer1 = new Integer(20);
int num2 = integer1;
System.out.println(num2);
}
//基本数据类型、包装类 --- >String类型,调用String重载的valueOf()
@Test
public void test4(){
int num = 10;
String s = String.valueOf(num);
System.out.println(s);
String s1 = String.valueOf(new Integer(20));
System.out.println(s1);
}
//String类型 ---> 基本数据类型、包装类
@Test
public void test5(){
String s = "123";
int i = Integer.parseInt(s);
System.out.println(i);
}
}
static: 静态的
static可以用来修饰: 属性、方法、代码块、内部类
使用static修饰属性: 静态变量 或者称为(类变量)
属性:按是否使用static修饰,又分为: 静态属性 vs 非静态属性(实例变量)
static修饰属性的其他说明
静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
静态变量的加载要早于对象的创建
由于类只会加载一次,则静态变量在内存中也只会存在一份: 存在方法区的静态域中。
例子
public class ChineseTest {
public static void main(String[] args) {
Chinese chinese = new Chinese();
Chinese chinese1 = new Chinese();
chinese1.name="法国";
System.out.println(Chinese.name);//通过类名.静态变量名调用 法国
System.out.println(chinese.name);//法国
System.out.println(chinese1.name);//法国 当静态变量值被修改,那么所有对象的值都会被修改,因为被static修饰的变量是共享的。
}
}
class Chinese{
//静态变量
static String name = "中国";
}
使用static修饰方法: 静态方法
static注意点:
开发中,如何确定一个属性是否要声明static
代码块的作用: 用来初始化类、对象
代码块如果有修饰的话,只能使用static
分类: 静态代码块和非静态代码块
静态代码块
内部可以有输出语句
随着类的加载而执行,只执行一次
调用类的静态方法加载静态的代码块,只执行一次。
static {
System.out.println("我是静态代码块");
}
作用: 初始化类的信息,例如被static修饰的属性、方法。
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块
内部可以有输出语句
随着对象的创建而执行,每创建一个对象,就会执行一次非静态代码块。
创建完对象才会加载非静态代码块,并且每造一个对象,都会加载一次非静态代码块
{
System.out.println("我是非静态代码块");
}
作用: 可以在创建对象时,对对象的属性或方法等进行初始化。
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
abstract关键字的使用
public class AbstractTest {
public static void main(String[] args) {
Student student = new Student();
student.eat();
}
}
abstract class Persion{
String name;
int age;
public Persion() {
}
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
}
class Student extends Persion{
@Override
public void eat() {
System.out.println("学生要吃有营养的饭");
}
}
接口的使用
接口使用interface来定义
Java中,接口和类是并列的两个结构,(同级别)
如何定义接口: 定义接口中的成员
JDK7及以前: 只能定义全局常量和抽象方法
JDK8: 除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(新特性)。
接口中定义的静态方法,只能通过接口来调用。也就是(接口名.方法名)。
通过实现类的的对象,可以调用接口中的默认方法。
如果实现类重写了接口的默认方法,调用时,仍然调用的是重写以后的方法。
如果子类继承父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。
子类的方法中调用接口中的默认方法,格式: 接口名.super.方法名
例子
public class InterfaceTest2 {
public static void main(String[] args) {
Animal.eat();//动物们要吃好吃的
new Cat().play();//狗在玩游戏
new Cat().run();//狗在跑步
new Cat().sleep();
}
}
interface Animal{
//静态方法
public static void eat() {
System.out.println("动物们要吃好吃的");
}
//默认方法
public default void play(){
System.out.println("动物们正在玩游戏");
}
public default void run(){
System.out.println("动物们在跑步");
}
public default void gram(){
System.out.println("动物们在玩游戏");
}
}
class Dog{
public void run(){
System.out.println("狗在跑步");
}
}
class Cat extends Dog implements Animal{
//重写之后的方法
public void play(){
System.out.println("猫在玩游戏");
}
public void sleep(){
System.out.println("狗在睡觉");
//调用接口中的方法
Animal.super.gram();
}
}
接口中不能定义构造器,意味着接口不可以实例化。
Java开发中,接口通过让类去实现(implements)的方式来使用
如果实现类覆盖了接口中的所有的抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
简单明了,如果一个类想使用一个接口,那么必须要重写接口里面的抽象方法。
例子
public class InterfaceTest {
public static void main(String[] args) {
Cat cat = new Cat();
System.out.println(cat.mode);
cat.eat();
}
}
interface Fly{
String mode = "老虎"; //省写了 public static final
void eat();//省写了 public abstract
}
class Cat implements Fly{
@Override
public void eat() {
System.out.println("猫吃猫粮");
}
}
Java类可以实现多个接口 -->弥补了Java单继承的局限性
格式一 class A extends B implements C,D
class Kit{
}
interface Wrench{
}
interface Insertion{
}
class Comprehensive extends Kit implements Wrench,Insertion{
}
格式二 class A implements B,C
interface Wrench{
}
interface Insertion{
}
class Comprehensive implements Wrench,Insertion{
}
接口与接口之间可以继承,而且可以多继承
interface Car{
}
interface Bike{
}
interface Foot extends Car,Bike{
}