Java第六课. 面向对象特征1
回顾:
1. 类(相同属性和方法的对象的集合,是一个模板),对象(现实世界中存在的可以被描述得事物)
2. 两者关系:类是对象的类型,对象是类的实例
3. 如何从一个类得到一个对象? 类名 对象名=new 类名()
4. 类的基本结构:
public class 类名{
//常见的属性和方法
}
5. 方法:有4种类型
//1.无参数无返回值
public void 方法名(){
//方法体-实现->每个方法都有自己特定功能->一个方法做一件事情
}
//2.无参数有返回值
public 返回值类型 方法名(){
//方法体-实现->每个方法都有自己特定功能->一个方法做一件事情
return 值;
}
//3.有参数无返回值
public void 方法名(类型 参数名,类型 参数名,类型 参数名){
//方法体-实现->每个方法都有自己特定功能->一个方法做一件事情
}
//4.有参数有返回值
public 返回值的类型 方法名(类型 变量名,类型 变量名,类型 变量名){
//方法体-实现->每个方法都有自己特定功能->一个方法做一件事情
return 值;
}
6.调用:
1.无参数无返回值
对象名.方法名();
2.无参数有返回值
有返回值的话需要定义一个变量去接收
类型 变量名=对象名.方法名();(变量的类型根据方法返回值的类型而定)
3.有参数无返回值
对象名.方法名(1,3,5);//参数个数和参数类型要对应
4.有参数有返回值
类型 变量名=对象名.方法名(1,3,5)
7.return
一个方法中的return最多只能执行一次,
return意味着这个方法的执行已经终止;这个方法的后续代码将不会执行;
1. 封装
1.1 封装的定义
封装的特性能够让服务提供者把它服务的细节隐藏掉,你只需要提交请求与传递它需要的参数,它就会给你返回结果,而这个结果是如何产生的,经过了多少复杂运算,经过多少次数据读取,你都不用管,只要它给你结果就好了。
1.2 封装的实现
将[属性私有化],属性的名字不对外公开,但是外部还要访问私有的属性,我们可以[提供公共的方法来访问私有的属性];
步骤:
1.将成员属性私有化
2.提供公共的 setter/getter方法
public class Boy {
//1.将属性私有化
private String name;
private int age;
//2.提供公共的方法来访问私有的属性
/**
* 取值的方法
* @return 属性值
*/
public String getName() {
return name;
}
/**
* 赋值的方法
*/
public void setName(String name1) {
//this指的是当前对象
this.name=name1;
}
测试类:
public class TestBoy {
public static void main(String[] args) {
Boy boy=new Boy();
//先赋值
boy.setName("君羊");
//再取值
System.out.println(boy.getName());
}
}
1.2 封装的作用
解决了数据的安全性问题,提供安全性,在setter中可以对外部传递进来的参数进行判断;
赋值(取值)过程中加一些判断;
public void setAge(int age) {
if (age<=0) {
System.out.println("年龄赋值有误:age将默认值赋值为1");
age=1;
}
this.age=age;
}
测试类:
Boy boy=new Boy();
//先赋值
boy.setName("君羊");
boy.setAge(-7);
//再取值
System.out.println(boy.getName());
System.out.println(boy.getAge());
快捷键:右键->sources…
2 [面试题]方法的重载
2.1 重载的定义
定义:在[同一个类中方法名相同,参数项不同],这时候方法之间的关系称为重载.
1.参数个数不同;
2.参数个数相同,数据类型或者类型顺序不同;
重载的好处:方法名的复用性.[构造方法]就是一个典型的方法重载;
[注意]:方法的重载规则与返回值无关;
2.2 练习
1.定义类,在类中定义自我介绍的方法,输出个人信息;包含姓名,性别,年龄,联系方式等;
分析: 将属性定义好,私有化,封装;方法中将个人信息打印出来:
public class Person {
private String name;
private String sex;
private int age;
private String address;
private String weChat;
//getter与setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getWeChat() {
return weChat;
}
public void setWeChat(String weChat) {
this.weChat = weChat;
}
//构造方法
public Person(String name, String sex, int age, String address, String weChat) {
this.name = name;
this.sex = sex;
this.age = age;
this.address = address;
this.weChat = weChat;
}
//输出个人信息,直接打印
public void printfInfo() {
System.out.println("我是:"+name+",性别:"+sex+",我今年"+age+"岁,我家在"+address+",欢迎大家来做客,请加微信"+weChat);
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + ", age=" + age + ", address=" + address + ", weChat=" + weChat
+ "]";
}
2.3 toString()
toString() 方法可把一个 Number 对象转换为一个字符串,并返回结果;
快捷键生成toString():右键:source->generate toString
测试类:
public class TestPerson {
public static void main(String[] args) {
//创建一个pserson对象
//方式1:
Person person=new Person("zzh", "男", 12, "东北", "110");
//方式2:
Person person2=new Person();
person.setName("zzh");
person.setSex("男");
person.setAge(18);
person.setAddress("东北");
person.setWeChat("110");
//调用自我介绍方法
person.printfInfo();
//1.有一个方法 toString(),有一个返回值,String,所以我们调用这个方法要接收返回值
String info=person.toString();
System.out.println(info);
//2.还可以不接收,直接调用方法,放在syso中调用
System.out.println(person.toString());
//3.toString()省略 ,所以打印一个对象名的时候,默认调用其toString()
System.out.println(person);
}
我是:zzh,性别:男,我今年18岁,我家在东北,欢迎大家来做客,请加微信110
Person [name=zzh, sex=男, age=18, address=东北, weChat=110]
Person [name=zzh, sex=男, age=18, address=东北, weChat=110]
Person [name=zzh, sex=男, age=18, address=东北, weChat=110]
//这里用set()方法给对象的属性赋值可能会有遗漏,所以这时候用构造方法给对象属性赋值较好,有遗漏就会报错,而且代码节俭;而且实例化对象的时候默认是调用了无参构造;
3. 构造方法
3.1 构造方法的特点、作用
1. [构造方法的方法名和类名一样];
2. [无返回类型];
判断以下方法是不是构造方法:
public class Pig {
//私有化构造方法
private Pig() {}
//公共的构造方法
public Pig() {}
//没有访问修饰符的构造方法
Pig(){}
//带参数同时私有化的构造方法
private Pig(String name) {}
//不是构造方法
private void Pig(String name) {}
//不是构造方法
private Pig Pig(String name) {}
//不是构造方法
public String Pig() {}
}
作用:
1. 用来实例化对象,调用无参[也可以有参构造方法];
2. 调用构造方法的时候同时希望对成员属性赋值,这个时候需要调用有参数的构造方法;
3.2 默认构造方法
1.正常情况下,定义一个类,这个类内部会有一个构造方法(构造器);
//默认存在的(无参构造)
public Person() {
}
3.3 使用构造方法创建对象
类名 引用名= new 构造方法()
类名 引用名= new 构造方法(参数列表)
//创建一个pserson对象
//方式1:
Person person=new Person("zzh", "男", 12, "东北", "110");
//方式2:
Person person2=new Person();
person.setName("zzh");
person.setSex("男");
person.setAge(18);
person.setAddress("东北");
person.setWeChat("110");
思考:
当我们在一个类中定义一个带参数构造方法的时候,那么这个类中,就有两个构造方法了?? 不对
[注意]:一个类中有一个默认的无参数构造方法,如果显式声明一个有参数构造方法,[这个时候无参构造方法就被覆盖掉了];
所以,如果既要无参又要有参,那么也要将无参构造方法显式出来;
public Person(String name, String sex, int age, String address, String weChat) {
this.name = name;
this.sex = sex;
this.age = age;
this.address = address;
this.weChat = weChat;
}
//默认存在的
public Person() {
}
3.5 构造方法重载
3.6 this关键字的作用
3.61 构造方法之间的相互调用
如果出现这种情况,在任何构造方法中this调用必须是第一个语句;
1.有参调用无参:
2.无参调用有参:
3.62 指代当前对象
//这里this指代的是调用方法的对象,并不是指当前的类;
练习:
1.定义一个动物类Animal,创建几个动物对象,模拟动物园Zoo有一群动物在表演节目;
/**
* 自定义动物类
* @author Administrator
*
*/
public class Animal {
//定义私密属性
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Animal(String name) {
//super();//调用父类的无参构造
this.name = name;
System.out.println("我是父类的带参构造");
}
//无参构造
public Animal() {
System.out.println("我是父类的无参构造");
}
/**
* 表演节目
* @param 节目
*/
public void show(String something) {
System.out.println(name+"在表演"+something);
}
public void eat() {
System.out.println(name+"在吃");
}
}
这里其实直接创建动物对象然调用方法也能实现,但是定义一个动物园类
//可以创建一个数组存放动物,这里看似代码变复杂了,但当动物变多了就会
//简便很多,而且起到了对数组封装的作用,我们所做的一切都是为了简化代码;
动物园类:1.定义数组去存放多只动物
2.将属性私有化,提供公开的去访问
3.在数组的set方法中去定义2只动物给数组赋值
4.表演的方法->数组中每一只动物的表演方式设定一下
public class Zoo {
/**
* 包含多种动物
*/
//数组语法:数据类型 数组名[]=new 数据类型[长度]
private Animal animals[]=new Animal[2];//null
//注意:这里的Animal它属于引用类型中的类,其实和int,String一样都是数
//据类型,只不过是我们自己定义出来的;后面我们也会经常用到;
public Animal[] getAnimals() {
return animals;
}
/**
* 在数组的set方法中去定义2只动物给数组赋值
*/
public void setAnimals() {
Animal animal01=new Animal("羊驼");//调用有参构造
Animal animal02=new Animal("熊猫");
//语法:数组名[索引]=值;
animals[0]=animal01;
animals[1]=animal02;
}
/**
* 表演的方法->数组中每一只动物的表演方式设定一下
*/
public void show() {
animals[0].show("吐口水~");
animals[1].show("先翻跟头,再卖萌~");
}
}
测试类:
public class TestZoo {
public static void main(String[] args) {
Zoo zoo=new Zoo();
//先创建动物
zoo.setAnimals();
//再表演节目
zoo.show();
}
}
我是父类的带参构造
我是父类的带参构造
羊驼在表演吐口水~
熊猫在表演先翻跟头,再卖萌~
3.7 类初始化代码块(static)
在之前我们已经了解到,Java代码是由Java虚拟机加载解释运行的,那么有没有可能捕获Java虚拟机对某个特定类的加载操作呢?也就是说如果我们能够在虚拟机加载某一个类时即可以触发某一个操作,那么我们就能够完成一些更为通用的信息初始化工作,这对于某一些功能组件显得尤为突出(如JDBC)
Java中的类初始化代码块能够实现这个目标;
类初始化代码块在类中编写,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,基础结构如下:
static{
代码段 //被static{}框定的代码段将在该类被加载时自动执行
}
对于一个包含main入口的类,要想运行本程序,就必须要加载这个类,因此这个类的static代码段首先被执行;
构建类对象时必须加载类;
当一个类中有多个static{}的时候,按照static{}的定义顺序,从前往后执行;先执行完static{}语句块的内容,才会执行调用语句;
虚拟机加载类后除非特殊情况(如虚拟机内存等因素)会将类卸载,一般情况下整个生命周期中,类都会只加载一次,又因为static{}是伴随类加载执行的,所以,不管new多少次对象实例,static{}都只执行一次;
3.8 实例初始化代码块
和类初始化代码块类似,Java还提供了实例初始化代码块,实例初始化代码块在类初始化代码块的基础上去掉了static关键字;
当创建Java对象时,Java虚拟机总是先调用该类里定义的初始化块,如果一个类中定义了多个初始化块,则按照定义的顺序执行,这个和类初始化块类似;
初始化块虽然也是Java类的一部分,但它没有名字,也就没有标识,因此无法通过类、对象来调用初始化块。初始化块只在创建Java对象时隐式执行,而且在执行构造器之前执行;
虽然可以定义多个代码块,但都是隐式执行,所以定义多个代码块没有多大意义;
如果有一段初始化代码对所有对象都相同,且无须接收任何参数,就可以把这段初始化代码处理代码提取到初始化块中。
和static代码块不同,每次实例化对象时均会执行一次实例初始化代码块,且示例初始化代码块中可以访问成员属性,并能够使用this引用;
3.9 初始化代码块和构造方法的运行顺序
通过以下代码可以发现初始化代码块和构造方法的执行顺序(在存在继承关系时会更为复杂一下,讲解继承时会详细讲解)
3.10 JDK8的 :: 关键字
在JDK8中,类的普通方法及构造方法能够通过::关键字被其他类的方法引用;
public class Jisuan{
public static void main(String[] args){
Arrays.asList(1,2,3).forEach(System.out::println)
}
}
4. 访问权限控制
4.1 包的概念与作用
• 为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间;
• [包的作用]:
• 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用
• 如同[文件夹一样],包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个 不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
• 包也提供了限定了访问权限的一个控制范围,拥有包访问权限的类才能访问某个包中的类
• Java 使用包这种机制是为了防止命名冲突,访问控制,提供搜索和定位类、接口、枚举和注解等,它把不同的 java 程序分类保存,更方便的被其他 java 程序调用;
• 以下是一些JDK中的包:
• java.lang:打包基础的类
• java.io:包含输入输出功能的函数
• java.util:包含一些重要的工具
• …
• 开发者可以自己把一组类等组合定义自己的包。而且在实际开发中这样做是值得提倡的,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注解等是相关的;
• 由于包创建了新的命名空间,所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单;
4.2 package与import关键字
• Java中用 package 语句来将一个Java源文件中的类打成一个包;
• package语句必须作为Java源文件的第一条语句,指明该文件中定义的类
所在的包(若忽略该语句,则指定为无名包)。它的格式为:
package pkg1[.pkg2[.pkg3…]];
• Java编译器把包对应于文件系统的目录管理;
• package语句中,用“ .” 来指明目录的层次;
• 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它;
• 为了能够使用其他包的成员,需要在 Java 程序中明确导入该包;
• 使用 "import" 语句可完成此功能;
• 在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2…].(classname|*);
• 如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略;
• import语句中类名部分可以使用通配符“*”
• 符号*表示直接导入包中所有的类
• 如:import com.chinasofti.* 表示导入com.chinasofti包中所有的类;
• 注意:包和子包之间不存在继承关系,只要两个类不直接在同一个文件中即认为位于不同的包,因此*号只能包含本包中的类而不能包含子包中的
类;
4.3 包的命名规则
• 创建包的时候,你需要为这个包取一个合适的名字,根据Java包的约定,名字内的所有字母都应小写,之后,如果非同包的其他的一个源文件使用了这个包提供的类、接口、枚举或者注释类型的时候,都必须在这个源文件的开头说明所引用的包名;
• 通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是 chinasofti.com,所有的包名都以 com.chinasofti 开头;
4.4 javac的-d参数
• 编写Java代码后在命令行通过javac进行编译时,如果没有特殊说明,即便代码中存在package语句声明了代码所在的包,也会直接在当前文件夹中直接生成.class文件(不会生成包路径层次结构);编译时添加-d参数,可以明确编译以后的结果保存的位置,并且会在该位置中自动生成对应的包路径结构;
4.5 类的访问控制符
• 有了包的概念之后,可以通过某种方式按照权限要求过滤对类的访问;
• 要控制类的访问,需要使用[访问控制符];
• 顶层类的访问级别:有两种(后续会内部类充当类成员时,针对该内部类可以使用类成员的访问控制符):
• 默认的(不提供访问控制符):仅可被同包的其他代码访问
• public: 可以被任何代码访问
4.6 类成员的访问控制符
• 除了类自身存在访问控制外,类的成员还存在更为精确的权限控制体系;
• 为了实现封装特性,可以通过对类成员的权限访问来隐藏数据而暴露操作接口(简单来说就是阻止对成员变量的直接操作而由暴露成员方法对数据
进行操作);
• 类成员的访问级别有四种:
• private
• 不允许任何其他类存取和调用;
• default(不使用 default 关键字,和类的 default 类似,不提供修饰符即为默认权限)
• 在同一程序包中出现的类才可以访问;
• protected
• 如果一个类中变量或方法有修饰字 protected ,同一类,同一包可使用。不同包的类要使用,必须是该类的子类(继承关系,后续 详细介绍);
• 需要注意的是,即便在非同包的子类中,也只能通过直接调用继承下来的成员的方式访问 protected 成员,而不能使用父类的引用进行调用;
• public
• 任何其它类对象,只要可以看到这个类的话,那么它就可以存取变量的数据,或使用方法;
• 不能单纯以成员访问控制符确定一个成员是否能够访问,如果类本身不能被访问,那么即便成员为 public 公开权限,也是不能被访问的;
4.7 总结
• Java中的类根据是否提供public关键字来划分为两种权限:公开的类能够被所有的其他类访问,而非公开的类(default)只能被同一个包中的其他类访问;
• Java中的封装是将通过控制成员变量访问权限,公开变量的读取和设置方法来实现的;
• 类成员的访问控制分为4中类型,分别是公开的、缺省的、受保护的和私有的: