什么是面向对象?来看集合篇,看完你就了解什么是面向对象。
学习了一周面向对象,一开始对于面向对象的概念我也觉得很抽象、很模糊,后来接触了一些项目后,才慢慢搞懂。下面是我整理出来关于面向对象的一些基础知识。
Java是一种面向对象的编程语言。面向对象编程,英文是Object-Oriented Programming,简称OOP。
面向对象(Object Oriented)是相对面向过程(procedure Oriented)来说的,它们都是软件开发的一种方式。
面向对象的概念和应用已超越了程序设计和软件开发,是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
那什么又是面向过程呢?
面向过程是关注流程的,每解决一个程序问题,都需要程序员逐步分析,然后再逐步实现。
举个例子:
小A饿了,需要吃饭。那么他就要自己去买菜、洗菜、淘米、煮饭、炒菜,最后吃饭,这是一个吃饭,解决饿的过程。在这个过程里呢,小A需要一步一步的去完成,最后才能吃到饭,解决饿的问题。这就是面向过程。
对于这种复杂的过程,面向对象就能更好的解决复杂问题。
面向对象是相对于面向过程来讲的,指的是把 相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。
举个例子:
小B饿了,需要吃饭。小B呢,不想自己做饭了,去饭店吃饭,解决饿的问题。小B不需要知道饭菜怎么做,只管好吃不好吃,能不能填饱肚子,这就是面向对象。
面向过程到面向对象思想层面的转变:
面向过程关注的是执行的过程,面向对象关注的是具备功能的对象。
面向过程到面向对象,是程序员思想上 从执行者到指挥者的转变。
面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP
OOA:面向对象分析(Object Oriented Analysis)
OOD:面向对象设计(Object Oriented Design)
OOP:面向对象程序(Object Oriented Programming)
面向对象有三大特征:封装、继承、多态。
封装性:所有的内容对外部不可见
继承性:将其他的功能继承下来继续发展
多态性:方法的重载本身就是一个多态性的体现
类表示一个共性的产物,是一个综合的特征;
而对象,是一个个性的产物,是一个个体的特征。
(相当于设计图纸与实物的概念)
比如说建一栋房子,你得先有设计图吧,有了设计图,工人才能根据设计的内容进行建造。那么设计图纸就相当于编程里的类,工人就相当于编程里的对象。
类必须通过对象才能使用,对象所有操作都在类中定义。
类由属性和方法组成:
属性:就相当于人的一个个的特征(如:勤劳、懒惰、忠诚)
方法:就相当于人的一个个的行为(如:吃饭、睡觉、学习)
类定义写在“ .java ”文件中
类的定义格式:
class 类名称{
成员属性
成员方法
}
一个“ .java ”文件中,可以存在N个类,但是只能存在一个public修饰的类
代码示例:
class Person{
//属性 - 特征
String name;//定义了一个字符串属性为name的特征
int age;//定义了一个整型属性为age的特征
char sex;//定义了一个字符型属性为sex的特征
}
一个
属性定义格式:
数据类型 属性名;
属性定义并赋值的格式:
数据类型 属性名 = 初始化值;
代码示例:
int age = 18;
方法定义格式:
返回值类型 方法名称(形式参数列表){
//方法体 :方法被调用时执行的代码
return 返回值;
关于方法:
1)返回值:方法体内向方法体外返回的数据内容,通过return关键字完成。如果一个方法一旦执行了return语句,就立即结束。返回值类型为void时,不用写return语句。
2) 返回值类型:返回值的数据类型,必须在方法定义时提前声明,可以是基本数据类型,也可以是引用型数据类型。如果不需要返回值,可以写void
3) 方法名称:需要给不同的方法赋予不同的名称。
4)形式参数列表:定义方法时,可以声明方法执行时所需的外部参数。参数可以多个,看具体方法需要。一个方法在定义时,还未被调用,所以是形式上的参数列表。参数格式:数据类型 名称 。当有多个参数时,用逗号隔开。
5)方法体:是方法中包含的多条程序语句。可以使用成员变量。
代码示例:
//返回类型为整型int 方法名为 sum 形式参数为整型a和b
int sum(int a,int b) {
int c = a + b;//方法体 对a和b求和,然后赋值给c
return c;//返回值为整型int“ c ”
}
对象的创建格式如下:
类名称 对象名称 = new 类名称();
对象创建完,就可以使用对象的属性和方法了,使用格式如下:
给类中属性赋值:对象名.成员变量名称 = 值;
访问类中的属性:对象名.成员变量名称
调用类中的方法:对象名.方法(实际参数列表)
下面编写一个拥有颜色(color)、品牌(brand)和价格(price)属性,并包含介绍方法(introduction)的汽车类(Car)。
首先是定义类
//汽车类(Car)
class Car{
//属性
String color; //颜色
String brand; //品牌
//介绍的方法
void show(){
System.out.println("汽车的品牌为:"+brand);
System.out.println("汽车的颜色为:"+color);
}
}
定义类后,编写一个入口程序来创建一个汽车(对象)
public class Object_01 {
public static void main(String[] args) {
Car car = new Car(); //创建一个汽车对象,对象名为car
car.color = "白色"; //给汽车car设置颜色color为"白色"
car.brand = "宝马"; //给汽车car设置品牌brand为"宝马"
//调用汽车car类show的方法
car.show();
}
}
构造方法也叫构造器,顾名思义就是构造一个方法,用构造方法去构造对象,并给对象的属性进行赋值。
在下面代码右侧Person后面出现的小括号, 其实就是在调用构造方法 !
Person p = new Person();
接下来说一下构造方法的作用、执行时机、特点等。
作用:用于对象初始化。
执行时机:在创建对象时,自动调用。
特点:
所有的java类中都会至少存在一个构造方法。
如果一个类中没有明确的编写构造方法,则编译器会自动生成一个无参的构造方法,构造方法中没有任何的代码。
如果自行编写了任意一个构造器,则编辑器不会再自动生成无参的构造方法。
构造方法的格式与普通方法基本相同,区别在于构造方法的方法名与类的名称完全相同,且构造方法无返回值类型的声明。格式如下:
修饰符 类名(参数列表){
//方法体
}
代码如下(示例):
public class Demo1 {
public static void main(String[] args) {
Person p = new Person();//打印一次
Person p1 = new Person();//打印一次
Person p2 = new Person();//打印一次
}
}
//类名
class Person{
//属性
String name;
int age;
//无参数构造方法
public Person(){
System.out.println("创建对象时,就已经被调用了。");
}
//全属性有参数构造方法
public Person(String name1, int age1) {
name = name1;
age = age1;
}
}
建议自定义无参构造方法,不要对编译器形成依赖,避免程序错误发生。
当类中有非常量成员变量(属性)时,建议提供两个版本的构造方法,一个是无参构造方法,一个是全属性做参数的构造方法。
当类中所有成员变量都是常量或者没有成员变量时,建议不提供任何版本的构造。
一个类中定义的方法名称相同就是方法的重载。
特点:
1、方法名称相同。
2、参数列表长度 或 参数类型 或 参数顺序不同。
3、方法的重载与返回值无关。
4、可以让程序在不同的需求下,通过传递不同的参数调用方法来完成某些具体的功能。
代码如下(示例):
public class Demo1 {
public static void main(String[] args) {
Math math = new Math();//创建对象
int sum1 = math.sum(2, 3);//赋值int型数据
double sum2 = math.sum(5.1,6.8);//赋值double型数据
System.out.println(sum1);//5
System.out.println(sum2);//11.899999999999999
}
}
//创建类
class Math{
//方法名为sum,参数类型为int
int sum(int a,int b){
int c = a + b;
return c;
}
//方法名为sum,参数类型为double
double sum(double a,double b){
double c = a + b;
return c;
}
//方法名相同,参数列表类型不同
double sum(int b,double a){
double c = a + b;
return c;
}
}
注意
:方法重载与返回值类型无关。
构造方法的重载其实和方法的重载很像。
一个类,可以存在多个构造方法。
参数列表的长度或类型不同即可完成构造方法的重载。
可以在不同的创建对象需求下,调用不同的方法来完成对象的初始化。
代码如下(示例):
public class Demo2 {
public static void main(String[] args) {
Person2 p = new Person2("张三",20);
p.show();//姓名:张三 年龄:20
Person2 p1 = new Person2("李四");
p1.show();//姓名:李四 年龄:0
}
}
//类名为:Person2
class Person2{
//属性
String name;//姓名
int age;//年龄
//构造方法1(无参数形式)
public Person2(){}
//构造方法2(一个参数形式)
public Person2(String name2) {
name = name2;
}
//构造方法3(全属性参数形式)
public Person2(String name2, int age2) {
name = name2;
age = age2;
}
//方法
void show(){
System.out.println("姓名:"+name+" 年龄:"+age);
}
}
在类person2中,三个构造方法的参数类型或列表长度都不相同,但构造方法名称相同,这样的情况就是构造方法的重载。
顾名思义没有对象名称的对象,就是匿名对象。
匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,
等待被GC(垃圾回收器)回收。
只使用一次的对象可以通过匿名对象的方式完成。
代码如下(示例):
public class Demo3 {
public static void main(String[] args) {
//没有名字的对象,就是匿名对象
new Person3("王五",25).show();
//姓名:王五 年龄:25
}
}
class Person3{
String name;
int age;
public Person3(String name3, int age3) {
name = name3;
age = age3;
}
void show(){
System.out.println("姓名:"+name+" 年龄:"+age);
}
}
注意
:如果一个对象要使用两次以上,就必须给创建对象赋值名称。
概述:封装的意义在于保护或者防止数据被其它数据随意访问,导致程序无意被破坏。
在编写程序代码时,首先考虑哪些数据可以封装为一个单位,将属性和方法确定后统一协调使用,大大优化模块间的相互作用。
封装原则:隐藏对象的属性和实现细节,仅对外公开访问法,并且控制访问级别。
为了便于理解,观察如下代码:
//描述人的类Person2
class Person2{
//属性
String name;//姓名
int age;//年龄
//说的方法
void say(){
System.out.println("我是:"+name+" 年龄:"+age);
}
}
上面的代码将Person2封装为一个整体。
将上述代码放到程序入口类
如下面代码:
public class Demo2 {
public static void main(String[] args) {
Person2 p = new Person2();
p.name = "小红";
p.age = -99;
p.say();
}
}
运行结果为:
我是:小红,年龄:-99岁。
进程已结束,退出代码 0
在上面这段代码中,整个流程下来程序是没有报错的,但是小红年龄-99岁,在现实中,显然是不合逻辑的。
那么针对这个问题,就可以选择用权限修饰符private
(私有的)将类的”age“属性年龄私有化,也就是外部无法直接访问年龄。
再提供一些可供外部调用数据和验证数据的方法,外部相当于间接操作类的年龄,最终问题解决。
改进代码如下:
//描述人的类Person2
class Person2{
//属性
String name;//姓名
//private修饰age,使其私有化,只有本类才能使用
private int age;//年龄
//可供外部调用的方式
public void setAge(int age2) {
//再通过传入的参数进行判断
if (age2 < 0 || age2 > 150){
age2 = 18;
System.out.println("年龄设置不合理,已自动修正为默认值18。");
}else {
age = age2;
}
}
//说的方法
void say(){
System.out.println("我是:"+name+",年龄:"+age+"岁。");
}
}
程序入口:
public class Demo2 {
public static void main(String[] args) {
Person2 p = new Person2();
p.name = "小明";
//p.age = -99;//如果继续使用会报错
p.setAge(-888);
p.say();
}
}
运行结果:
年龄设置不合理,已自动修正为默认值18。
我是:小明,年龄:18岁。
进程已结束,退出代码 0
提示:在以后的开发中,为了避免出现类似的逻辑错误,建议对所有属性进行封装,也就是加以private
(私有的),并为其提供Setter和Getter方法进行设置和取得数据操作。
在Java基础中,this
关键字是一个最重要的概念。 使用this
关键字可以完成一下操作:
调用类中的属性
调用类中的方法或构造方法
表示当前对象
观察下面一段代码(示例):
public class Person{
private int age;
public void setAge(int age){
age = age;
}
}
在setAge方法体中,我们的想通过方法参数的age传进来赋值给属性age。
但是在执行过程中呢,其实是方法的参数age传给本身参数age,那么这段代码是没有意义的。
那么如何解决这个问题呢。那就是使用Java中的关键字this
来表示当前对象,调用当前对象的属性。
添加this后代码如下(示例):
public class Person{
private int age;
public void setAge(int age){
this.age = age;
}
}
static
表示“静态”的意思,可以用来修饰成员变量和成员方法。
static
的主要作用在与创建独立于具体对象的域变量或者方法。
通俗来说:
被static
关键字修饰的方法或者变量不需要依赖于对象来进行访问
只要类被加载了,就可以通过类名去进行访问。
并且不会因为对象的多次创建,而在内存中建立多份数据。
代码如下(示例):
public class Demo3 {
public static void main(String[] args) {
Person p1 = new Person("小明","北京");
Person p2 = new Person("小三","北京");
Person p3 = new Person("小李","北京");
Person p4 = new Person("小红","北京");
p1.say();
p2.say();
p3.say();
p4.say();
}
}
class Person{
String name;//姓名
String place;//地方
public Person(String name,String place) {
this.place = place;
this.name = name;
}
void say() {
System.out.println("姓名:"+name+",居住:"+place);
}
}
运行结果是这样的:
姓名:小明,居住:北京
姓名:小三,居住:北京
姓名:小李,居住:北京
姓名:小红,居住:北京
进程已结束,退出代码 0
观察代码就会发现,这些人都在“北京”这个地方,那么同一个地方就引用了4次,这显然会造成内存浪费。针对这个问题,就可以用static
去解决了。
格式:
类名.静态成员变量 = 数值;
类名.静态方法名();
添加static后的代码(示例):
public class Demo3 {
public static void main(String[] args) {
//不需要创建对象就能调用plac
Person.place = "北京";
Person p1 = new Person("小明");
Person p2 = new Person("小三");
Person p3 = new Person("小李");
Person p4 = new Person("小红");
p1.say();
p2.say();
p3.say();
p4.say();
}
}
class Person{
private String name;//姓名
//只要类被加载,就可以访问被static修饰的成员变量
static String place;//地方
public Person(String name,String place) {
this.place = place;
this.name = name;
}
public Person(String name){
this.name = name;
}
void say() {
System.out.println("姓名:"+name+",居住:"+place);
}
}
用static
修饰方法名也是同理:
添加static后的方法代码(示例):
public class Demo3 {
public static void main(String[] args) {
//不需要创建对象就能调用类的方法
Person.say();
}
}
class Person{
//用static修饰方法名
static void say() {
System.out.println("床前明月光,疑是地上霜。");
}
}
注意:
静态成员,在加载类时加载并初始化。
无论一个类存在多少个对象,对象的属性,在内存永远中只有一份。
在调用时,静态不能访问非静态,非静态可以访问静态。
代码块就是在程序中编写代码的区域,通常用大括号{}表示代码块。
代码块又分为普通代码块、构造代码块、静态代码块等。
在执行的流程中出现的代码块,称其为普通代码块。
代码(示例):
public class Demo1 {
public static void main(String[] args) {
{
/**
* 编写在执行顺序的代码流程中的代码块
* 就是普通代码块
*/
}
}
}
在类中的成员代码块,称其为构造代码块,在每次对象创建时执行,执行在构造方法之前。
代码(示例):
class Person1{
private String name;
private int age;
//构造代码块,随着对象的每次创建,执行一次
//且执行在构造方法之前
{
System.out.println("这里是构造代码块");
}
//无参构造方法
public Person1(){
System.out.println("创建对象时执行");
}
//全参构造方法
public Person1(String name, int age) {
this.name = name;
this.age = age;
}
}
构造代码块区别于构造方法的是:
无论你调用哪一个构造方法来创建对象,构造代码块都必须执行一次。
在类中使用static修饰的成员代码块,称其为静态代码块。
在类加载时,静态代码块执行。
每次程序启动到关闭,只要类被加载了,就始终只执行一次静态代码块。
代码(示例):
public class Demo1 {
public static void main(String[] args) {
{
/**
* 编写在执行顺序的代码流程中的代码块
* 就是普通代码块
*/
}
Person1 p;
Person1 p0 = new Person1();
Person1 p1 = new Person1();
Person1 p2 = new Person1();
}
}
class Person1{
private String name;
private int age;
//构造代码块
{
System.out.println("这里是构造代码块");
}
static {
System.out.println("静态代码块执行且执行一次");
}
//无参构造方法
public Person1(){
System.out.println("创建对象时执行");
}
//全参构造方法
public Person1(String name, int age) {
this.name = name;
this.age = age;
}
}
运行结果:
静态代码块执行且执行一次
这里是构造代码块
创建对象时执行
这里是构造代码块
创建对象时执行
这里是构造代码块
创建对象时执行
进程已结束,退出代码 0
重点:
构造方法与构造代码块以及静态代码块的执行顺序:
静态代码块 > 构造代码块 > 构造方法
设包是Java语言提供的一种组织代码文件和管理代码文件的技术。
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
包就跟文件夹一样,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。这样就可以避免一些类名字冲突的情况。
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
包的命名规则遵循标识符命名规则,同时包名也有自己的命名规范:
通常由多个单词组成。
包名单词全部小写。
如果有多个单词,单词之间使用英文( . )分割。
包中java文件的定义:
在.java文件的首部,必须编写类所属哪个包。
格式:
package 包名;
注意:
如果想要跨包访问某个类的成员,
则此类的成员需要使用权限修饰符“public”
Java中默认的导入包是“java.lang”包,其中包含了“String”类、“System”类、“Math”类等常用类。
格式:
import
包名.类名;
代码(示例):
package test725;
import java.util.Scanner;
public class outTest {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
}
}
在上面一段代码中,如果需要从控制台获取用户输入内容时,就需要导入Scanner包。
权限修饰符可以访问我们在Java需要的资源,比如类的方法、类的属性、构造方法等。
下面是4种权限修饰符的一些使用范围。
修饰符 | 类 | 包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
继承是java面向对象编程技术的一块基石,因为它允许创建等级层次的类。
继承就是子类继承父类的特征和行为
使得子类对象(实例)具有父类的实例域和方法
或子类从父类继承方法,使得子类具有父类相同的行为
继承格式:
//格式:
class 父类{
}
class 子类 extends 父类{
}
代码(示例):
public class Demo4 {
public static void main(String[] args) {
//创建子类的对象
Student4 s = new Student4();
s.setName("小明");//调用父类的setName设置姓名
s.setAge(18);//调用父类的setAge设置年龄
s.say();//调用父类的方法
}
}
//格式:
class Person4{
private String name;
private int age;
public Person4(){
super();
}
public Person4(String name, int age) {
super();
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 say(){
System.out.println("你好!我是:"+name+", 今年:"+age+"岁。");
}
}
/**
* 继承用到 extends关键字
*/
class Student4 extends Person4{
//子类可以继承父类的特征和行为
}
继承的限制:
Java中只有单继承、多重继承,没有多继承。
super
是在子类对象被创建时,先在内存创建父类对象,创建完父类对象后会在子类自动创建super
。
super就相当于是父类对象的内存地址,子类通过super也就是内存地址去访问父类对象相关资源。
如下图所示:Person作为父类对象,Student作为子类对象,在Student中自动生成super
,而super
对应父类对象的内存地址0x123,这样子类就可以通过super
去访问父类对象的属性name和age了。
通过super
:
可以访问父类构造方法 、属性 、方法
调用super
构造方法的代码,必须写在子类构造方法的第一行。
代码(示例):
class Student4 extends Person4{
//子类构造方法
public Student4(){
//super必须写 在构造方法第一行
super("张三",18);//访问构造方法
super.sex = "男";//访问属性(父类的属性不能是private)
super.say();//访问方法
}
}
重写主要是用在子类继承父类的方法时,父类的方法不符合某些要求,需要对该方法进行重写。
重写(override)规则:
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问权限不能比父类中被重写的方法的访问权限更低;
父类的成员方法只能被它的子类重写;
声明为static和private的方法不能被重写,但是可以再次声明。
代码(示例):
public class Demo5 {
public static void main(String[] args) {
Student s = new Student();
s.say();//输出:西出阳关无故人
}
}
//父类
class Person{
public void say(){
System.out.println("锄禾日当午,汗滴禾下土。");
}
}
//子类
class Student extends Person{
//重写父类say()
public void say(){
System.out.println("西出阳关无故人");
}
}
重点:
重写与重载的区别:
区别 | 重写(Override) | 重载(Overload) |
---|---|---|
发生的位置 | 字父类中 | 一个类中 |
参数列表限制 | 必须相同 | 必须不同 |
返回值类型 | 必须一致 | 无关 |
访问权限 | 子类不能小于父类 | 无关 |
异常处理 | 范围更小,不能抛出新的异常 | 无关 |
final
关键字:
1.final用于修饰属性、变量
变量成为了常量,无法对其再次进行赋值
final修饰的局部变量,只能赋值一次(也可先声明后赋值一次)
final修饰的是成员属性,必须在声明时赋值
代码(示例):
final int a = 10;
final int b;//先声明
b = 20;//只能赋值一次
2.final用于修饰类
final修饰的类,不可以被子类继承
代码(示例):
final class Person{
//被final修饰后,Person不能被继承
}
3.final用于修饰方法
final修饰的方法,不能被子类重写
代码(示例):
class Person{
//被final修饰的方法,不能被子类声明
final public void say(){
System.out.println("锄禾日当午,汗滴禾下土。");
}
}
抽象类必须使用abstract class
声明
一个抽象类中可以没有抽象方法。
抽象方法必须写在抽象类或接口中。
代码格式:
abstract class 类名{
//抽象类
}
只声明而未实现的方法成为抽象方法。
未实现指的是:没有{}
方法体。
抽象方法必须使用abstract
关键字声明。
代码格式:
abstract class 类名{//抽象类
//抽象方法,只声明,不实现具体内容
public abstract void 方法名();
在抽象类的使用中有几个原则:
抽象类本身是不能直接进行实例化操作的,即:不能只用关键字
new
完成。一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。
1、抽象类能否使用final
声明?
答:不能。因为final
属于修饰的类是不可以被继承,即不饿有子类的,而抽象类必须有子类才有意义,所以不能使用final
声明。
2、抽象类能否有构造方法?
答:有构造方法。而且子类对象实例化的流程与普通类的继承是一样的,都是要先调用父类中的构造方法,之后再调用子类自己的构造方法。
1、抽象类必须用public
或protected
修饰,如果为private
修饰,那么子类则无法继承,也就无法实现其抽象方法。
2、抽象类不可以使用new
关键字创建对象,但是在子类创建对象时,抽象父类也会被JVM实例化。
3、如果一个子类继承抽象类,那么必须实现其所有的抽象方法。
如果有未实现的抽象方法,那么子类也必须定义为abstract
类。
如果一个类中的全部方法都是抽象类,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口。
定义格式:
interface 接口名称{
全局常量;
抽象方法;
}
这种思想是定义(规范、约束)与实现(名实分离的原则)的分离。
优点:
降低程序的耦合性
易于程序的扩展
有利于程序的维护
因为接口本身都是由全局常量和抽象方法组成,所以接口中的成员定义可以简写:
1、全局常量编写时,可以省略public static final 关键字
例如:
public static final String NAME = "张三";
简写后:
String NAME = "张三";
2、抽象方法编写时,可以省略public abstract 关键字,
例如:
public abstract void say();
简写后:
void say();
接口的实现跟抽象很类似,但是又不完全同于抽象。
在接口的实现中,需要用到implemnts
关键字。
接口可以进行多实现操作:
格式:
class 子类 implements 父接口1,父接口2...{
}
以上的代码成为接口的实现,那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:
class 子类 extends 父类 implements 父接口1,父接口2...{
}
接口因为都是抽象抽象部分,不存在具体的实现,所以允许多继承。
例如:
interfacce C extends A,B{
}
注意:
一个接口想要使用,必须依靠子类。
子类(如果不是抽象类的话)要实现接口中的所有抽象方法。
1、抽象类要被子类继承,接口要被类实现。
2、接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
3、接口里定义的变量只能是公共的静态常量,抽象类种的变量是普通变量。
4、抽象类使用继承来使用,无法多继承。接口使用实现来使用,可以多实现。
5、抽象类中可以包含static方法,但是接口中不允许。(静态方法不能被子类重写,因此接口中不能声明静态方法)
6、接口不能有构造方法,但是抽象类可以有。
这里写一个简单的代码示例:
接口1(Person):
//接口
public interface Person {
void say();
}
接口2(Person1):
public interface Person1 {
String name = "张三";
int age = 18;
}
子类1(单实现):
public class Docter implements Person {
@Override
public void say() {
System.out.println("我是医生");
}
}
子类2(多实现):
public class Student4 implements Person,Person1{
@Override
public void say() {
System.out.println("我叫:"+name+",今年"+age);
}
}
程序入口测试:
public class Demo4 {
public static void main(String[] args) {
Student4 s = new Student4();
say(s);
}
public static void say(Person p){
p.say();
}
}
运行结果:
我叫:张三,今年18
我是医生
Process finished with exit code 0
多态就是对象的多种表现形式。
对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态,对象多态性就是这样来的。
注意:
方法的重载和重写也是多态的一种,不过是方法的多态。
重载:一个类中方法的多态性体现。
重写:子父类中方法的多态性体现。
多态的使用也就是对象的类型转换。
格式1:
向上转型:将子类实例变为父类实例
父类 父类对象 = 子类实例;
//代码(示例):
Student s = new Student();
Person p = s;
p.say();
格式2:
向下转型:将父类实例变为子类实例
子类 子类对象 = 父类实例(子类)
//代码(示例):
Student s = new Student();
Person p1 = s;
Student s1 = (Student)p1;
s1.say();
instanceof
关键字:
作用:判断某个对象是否是指定类的实例,则可以使用instanceof
关键字
格式:
实例化对象 instanceof 类 //此操作返回boolean类型的数据
代码(示例):
public class Demo4 {
public static void main(String[] args) {
//创建学生类
Student4 s = new Student4();
say(s);//打印:我叫:张三,今年18
//创建医生类
Docter d = new Docter();
say(d);//打印:必须传进Student4形态类可以。
//因为医生类是跟学生类同一级,故不能把医生类强制转换为学生类
//只能把医生类转化为Person类,而不能转为Student类。
}
public static void say(Person p){
//判断传入的对象是此类的哪种形态
if(p instanceof Student4){
Student4 s = (Student4)p;
s.say();
}else{
System.out.println("必须传进Student4形态类可以。");
}
}
}
Object
类是所有类的父类,如果一个类没有明确的继承某一个具体的类,则将默认继承Object
类。
例如我们定义一个类:
public class Person{
}
当没有其它继承时,它其实在使用过程中是这样的:
public class Person extends Object{
}
使用Object
可以接受任意的引用数据类型
代码(示例):
public class Demo1 {
public static void main(String[] args) {
String name = "张三";
say(name);
int age = 18;
say(18);
}
//使用Object可以接收任何数据
public static void say(Object o){
System.out.println(o);
}
}
简单的说,Object
是Java中的万物,它在java语言中有很多方法、比如toString()、equals()、hashCode()等等。
在程序中,一般建议在所有类中都去重写Object
中的toString
方法。
此方法的作用:返回对象的字符串表示形式。
Object
的toString
方法,返回对象的内存地址。
代码(示例):
public class Person6 {
private String name;
private int age;
//toString方法
@Override
//重写后可以根据自己下需求去更改方法
public String toString() {
return "Person6{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
小提示:
在IDEA编辑器中使用快捷键Alt+Insert
可快速找到toString
方法,如下图,
在eclipse中的快捷键是Alt+Shift+s
。
建议重写Object
中的equals(Object obj)
方法。
此方法的作用:指示某个其他对象是否“等于”此对象。
Object
的equals
方法:
实现了对象上最具区别的可能等价关系;
也就是说,对于任何非空引用值x和y,
当且仅当x和y引用同一对象(x == y具有值true)时,此方法返回true
equals
方法重写时的五个特性:
自反性:对于任何非空的参考值x, x.equals(x)应该返回true
对称性:对于任何非空引用值x和y,x.equals(y)应该返回true当且仅当y.equals(x)回报true
传递性:对于任何非空引用值x,y和z,如果x.equals(y)回报true,而y.equals(z)回报true,那么有x.equals(z)同样返回true
一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false,前提是未修改对象上equals上比较中使用的属性。
非空性:对于任何非空的参考值x,x.equals(null)应该返回false
案例:使用equals()完成随机数的比较
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
广泛意义上的内部类一般来说包括这四种:
成员内部类是最普通的内部类,它的定义为:
位于另一个类的内部
代码(示例):
class Outer {
private String name = "张三";
private int age = 18;
public Outer(String s){
this.s = s;
}
//内部类
class Inner {
//内部类的写法和外部类没有太大的区别
private String name = "李四";
private int age = 20;
public void say() {
System.out.println("我叫:"+name+",今年"+age+"岁");
System.out.println("我叫:"+Outer.this.name+",今年"+Outer.this.age+"岁");
}
}
}
特点:
成员内部类可以无条件访问外部类的所有成员属性和成员方法,包括private
成员和静态成员。
注意:
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,也就说在访问同名情况下,调用的是成员内部类的成员。
如果需要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
外部使用成员内部类格式:
Outer o = new Outer();
Outer.Inner i = o.new Inner();
代码(示例):
public class Demo8 {
public static void main(String[] args) {
Outer o = new Outer();
Outer.Inner i = o.new Inner();
i.say();
}
}
运行结果:
我叫:李四,今年20岁
我叫:张三,今年18岁
局部内部类是定义在一个方法或者一个作用域里面的类。
它和成员内部类的区别:
在于局部内部类的访问仅限于方法内或者该作用域内。
class Person7{
public Person7(){}
}
class Man{
public Man(){}
public Person7 getPerson(){
class Student7 extends Person7{//局部内部类
String name = "王五";
}
return new Student7();
}
}
注意:
局部内部类就像是方法里面的一个局部变量一样,
是不能有public
、protected
、private
、以及static
修饰符的。
匿名内部类由于没有赋予名字,所有它的创建方式有点奇怪
创建格式:
new 父类构造器(参数列表) |实现接口(){
//匿名内部类
}
在这里我们看到使用匿名内部类,必须要继承一个父亲类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。
在使用匿名内部类的过程中,我们需要注意以下几点:
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且他不能使用外部类的非static成员变量或者方法。
格式:
class Outer{
public Outer(){
}
static class Inner{//静态内部类
public Inner(){
}
}
}
在Java中有一个设计的原则“一切皆对象”,那么这样一来Java中的一些基本数据类型,就完全不符合这种设计思想,因为Java中的八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题,引入了八种基本数据类型的包装类。
序号 | 基本数据类型 | 包装类 |
---|---|---|
1 | int | Integer |
2 | char | Character |
3 | float | Float |
4 | double | Double |
5 | boolean | Boolean |
6 | byte | Byte |
7 | short | Short |
8 | long | Long |
以上的八种包装类,可以将基本数据类型按照类的形式进行操作。
上面八种包装类又分为两种大的类型Number和Object。
将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。
将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作。
以上就是我对面向对象知识整理做一些笔记,或许有些地方写得还没有那么全面,欢迎指出,给出合理性的建议。
这篇算是我第一次做知识方面的笔记,希望在某些知识点上可以帮到大家。