Firstday
面向对象的多态性
1. Object类的toString()与equals(Object obj)方法
2. 面向对象的多态性
3. 抽象类与接口
如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类
如果直接打印某对象的引用,则默认会调用这个对象的toString()方法,默认打印的内容中包含这个引用所指向的内存地址。
可以根据需要在用户自定义类中重写toString()方法。
public String toString() {
return "Student [name=" + name + ", age=" + age + ", score=" + score
+ "]";
}
}
Object的equals方法定义为:x.equals(y),当x和y指向同一个地址时返回true,否则返回false。
String类中已经重写了equals(Object obj)方法,重写后的方法比较的是两个字符串的”内容”是否一样(注意:==比较对象的引用)。
可以根据需要在用户自定义类型中重写equals方法。
public class EqualsDemo {
public static void main(String[] args) {
Student stu1=new Student("张三",20,83.5);
Student stu2=new Student("张三",20,83.5);
System.out.println("stu1与stu2相等吗?"+stu1.equals(stu2));
//new相当于开辟了俩新地址,所以这里输出结果为false
}
}
public class StringEquals {
public static void main(String[] args) {
String str1="hello";
String str2="hello";
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
System.out.println("*********************************");
String str3=new String("hello");
System.out.println(str1==str3);
System.out.println(str1.equals(str3));
}
}
方法的重载与重写
重载(overloading):根据传入的参数不同,完成的功能也不同
重写(override):子类根据需求重写父类中的方法。
对象的多态性主要分为两种:
向上转型: 子类对象->父类对象 (程序会自动完成)
格式:
父类 父类对象=子类实例
向上转型后,因为操作的是父类对象,所以无法找到在子类中定义的新方法;但如果子类重写了父类的某个方法,则调用的是重写后的方法。
向下转型:父类对象->子类对象 (必须明确的指明要转型的子类类型)格式:
子类 子类对象=(子类)父类实例
注意:向下转型前先要向上转型。
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void say() {
System.out.println("我是父类方法say(),姓名:" + this.name + ";年龄:" + this.age);
}
public void learn(){ //父类方法
System.out.println("父类的learn()方法,人学习...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends Person {
private double score;
public Student() {
}
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
@Override
public void learn() { //子类方法重写,多态性的体现
System.out.println("学生学习...");
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
public class TestUpper {
public static void main(String[] args) {
Student stu=new Student("张三",20,83.5);
Person per=stu; // 向上转型 父类 父类对象=子类实例
per.say();
per.learn();
}
}
public class TestDown {
public static void main(String[] args) { 注意:向下转型前先要向上转型。
Person per=new Student("张三",20,83.5); // 向上转型
Student stu=(Student)per; // 向下转型 子类 子类对象=(子类)父类实例。
stu.learn();
}
}
格式:
对象 instanceof 类 返回boolean类型
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void say() {
System.out.println("我是父类方法say(),姓名:" + this.name + ";年龄:" + this.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 class Farmer extends Person{
public Farmer() {
}
public Farmer(String name, int age) {
super(name, age);
}
public void plant() {
System.out.println("农民种地...");
}
}
public class Student extends Person {
private double score;
public Student() {
}
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public void learn() {
System.out.println("学生学习...");
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
public class InstanceofDemo {
public static void main(String[] args) {
Student stu=new Student();
Farmer f=new Farmer();
method(null);
method(stu);
method(f);
System.out.println("********************");
Person p=new Student();
System.out.println(p instanceof Person); // 判断是否是Person类型
System.out.println(p instanceof Student); // 判断是否是由Student类的对象转换而来
}
public static void method(Person per){
if(per==null){
System.out.println("不能直接传递null!");
return;
}
if(per instanceof Student){
Student stu=(Student)per;
stu.learn();
}
if(per instanceof Farmer){
Farmer f=(Farmer)per;
f.plant();
}
}
}
注意:
1.final修饰变量(成员变量或局部变量),则成为常量,只能赋值一次
final 类型 variableName;
修饰成员变量时,定义时同时给出初始值,而修饰局部变量时不做要求。
final 返回值类型 methodName(paramList)
{
…
}
3. final修饰类,则类不能被继承
final class finalClassName{
…
}
public class Person{
private final String NAME="张三"; //final修饰属性
public void testFinal(){
final int a;
a=5;
System.out.println(a);
}
public final void walk(){ //final修饰方法
System.out.println("人用两条腿走路~~~");
}
}
abstract class 抽象类名{
}
3.2.抽象类不能被实例化,必须被继承,抽象方法必须被重写,生成它的子类。
抽象类不一定要包含抽象方法,若类中包含了抽象方法,则该类必须被定义为抽象类
如果一个子类没有实现抽象父类中所有的抽象方法,则子类也成为一个抽象类。
构造方法、静态方法、私有方法、final方法不能被声明为抽象的方法。
//父类
package abstractdemo;
public abstract class Animal {
private String name;
private String food;
public Animal(){
}
public Animal(String name, String food) {
this.name = name;
this.food = food;
}
public abstract void eat(); // 抽象方法只声明,不实现
}
//猫类
package abstractdemo;
public class Cat extends Animal {
private String fur;
public Cat(){
}
public Cat(String name, String food,String fur){
super(name,food);
this.fur=fur;
}
@Override
public void eat() {
System.out.println("小猫吃鱼...");
}
public String getFur() {
return fur;
}
public void setFur(String fur) {
this.fur = fur;
}
}
//test类
package abstractdemo;
public class TestAnimal {
public static void main(String[] args) {
Animal animal1=new Cat(); // 由猫对象向上转型
animal2.eat();
}
}
注意:
package abstractdemo;// 如果子类也是一个抽象类,则可以不用重写父类的抽象方法
public abstract class Tiger extends Animal{
public abstract void run();
}
接口是一种“标准”、“契约”。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只能包含常量和方法的定义,而没有变量和方法的实现。
接口体包括常量定义和方法定义
常量定义: type NAME=value; 该常量被实现该接口的多个类共享; 具有public ,final, static的属性.
方法体定义:具有 public和abstract属性
一个类可以实现多个接口,在implements子句中用逗号分开
非抽象子类必须实现接口中定义的所有方法
实现格式:
class 子类 implements接口A,接口B…{
}
在接口中声明方法时,不能使用static,final,synchronized,private,protected等修饰符。
一个接口可以继承自另一个接口。
java中不允许类的多继承,但允许接口的多继承。
接口中可以有数据成员,这些成员默认都是public static final
(1)通过接口实现不相关类的相同行为,而无需考虑这些类之间的关系.
(2)通过接口指明多个类需要实现的方法
(3)通过接口了解对象的交互界面,而无需了解对象所对应的类
eg:
/*
* 散步接口
* */
public interface Walk {
public void walk();
}
package interfacedemo;
/**
*
* 飞翔接口
*
*/
public interface Fly {
public static final int SPEED=200; // 常量
public abstract void fly();
}
public class Bird implements Fly,Walk{ //继承俩接口
@Override
public void fly() {
System.out.println("小鸟飞翔...");
}
@Override
public void walk() {
System.out.println("小鸟散步...");
}
}
public class Kite implements Fly{
@Override
public void fly() {
System.out.println("风筝飞翔...");
}
}
public class Test {
public static void main(String[] args) {
Fly f1=new Kite();
f1.fly();
Fly f2=new Bird();
f2.fly();
Walk w=new Bird();
w.walk();
}
}
public abstract class Animal {
}
import interfacedemo.Fly;
public interface ChildInterface extends ParentInterface,Fly{
}
import interfacedemo.*;
public class Dog extends Animal implements Walk{
@Override
public void walk() {
System.out.println("小狗散步...");
}
}
学习目标:
1.基本数据类型的包装类
2.String类、StringBuffer类、StringBuilder类
3.内部类
一、基本数据类型的包装类
(1)包装类、基本类
例如:char 包装类:java.lang.Character
int 包装类:java.lang,integer
byte,short,long,flolat,double,boolean则为:java.lang.Byte //首字母大写
转换:
基本数据类型转换为包装类:Integer intValue = new Integer(21);
包装类转换为基本类型:Integer integerId=new Integer(25);
int intId=integerId.intValue();
(2)自动装箱、拆箱
装箱:将基本数据类型包装为对应的包装类对象
拆箱:将包装类对象转换成对应的基本数据类型
package boxing;
public class IntegerDemo {
public static void main(String[] args) {
System.out.println("int的最大值:"+Integer.MAX_VALUE);
System.out.println("int的最小值:"+Integer.MIN_VALUE);
Integer a=100; // 自动装箱
int b=a; // 自动拆箱
String str="123";
int temp=Integer.parseInt(str); // 将字符串转换为int
System.out.println(temp+5);
}
}
public class CharacterDemo {
public static void main(String[] args) {
Character c='a'; // 自动装箱
char x=c; // 自动拆箱
System.out.println("变为大写:"+Character.toUpperCase('a'));
System.out.println("变为小写:"+Character.toLowerCase('Y'));
}
}
(1) String代表字符串类型,字符串的内容本身不可改变,字符串存储于“字符串常量池”中。
(2)String的两种实例化方式
a:直接赋值法: eg: String str=“Hello World”;
b:通过new操作符实例化: eg: String str=new String(“Hello World”);
所谓匿名对象,就是在堆内存中开辟了空间,但在栈内存中并没有引用指向的对象。
public class StringMethod {
public static void main(String[] args) {
String str="中华人民共和国";
System.out.println(str.charAt(2)); // 根据索引位置获取字符串中的某个字符
System.out.println(str.contains("人民")); // 判断当前对象代表的字符串是否包含参数字符串内容
System.out.println(str.indexOf("民共和"));
// 将"abcde"的d变为大写
String s1="abcdedx";
System.out.println("从后往前找d:"+s1.lastIndexOf("d"));
String s2="wxYz";
System.out.println(s2.toUpperCase());
System.out.println(s2.toLowerCase());
System.out.println("中华人民共和国".substring(3));
System.out.println("中华人民共和国".substring(3,6));
String s3=" 中 国 人 ";
System.out.println("s3的长度:"+s3.length());
System.out.println("删除了前后空格后的长度:"+s3.trim().length());
System.out.println("*************************************");
String country="中国,美国,俄罗斯,意大利,瑞典";
String[] array=country.split(",");
System.out.println("遍历拆分后的字符串:");
for(String cou:array){
System.out.print(cou+" ");
}
}
}
(1)StringBuffer代表可变的字符序列。
StringBuffer称为字符串缓冲区,它的工作原理是:预先申请一块内存,存放字符序列,如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。
public class StringBufferDemo {
public static void main(String[] args) {
StringBuffer sb=new StringBuffer();
sb.append("hello");
long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
sb.append(i);
}
long end=System.currentTimeMillis();
System.out.println("使用StringBuffer耗时:"+(end-start));
System.out.println(sb.length());
// sb.append("abc").append(false).append(12.5);
}
}
public class StringDemo {
public static void main(String[] args) {
String str="hello";
// 会产生大量的“垃圾”,而且不断“断开-连接”消耗性能
long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
str+=i; // str=str+i
}
long end=System.currentTimeMillis();
System.out.println("使用String耗时:"+(end-start));
System.out.println(str.length());
}
}
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer sb=new StringBuffer();
sb.append("中国").append("香水").append("Java");
System.out.println("反转之前:");
System.out.println(sb.toString());
sb.reverse(); // 反转缓冲区的内容
System.out.println("反转之后:");
System.out.println(sb.toString());
StringBuffer sb2=new StringBuffer();
sb2.append(false).append(12.5).append("hello");
sb2.insert(1,true); // 在索引为1处插入true
System.out.println(sb2);
}
}
StringBuilder和StringBuffer功能几乎是一样的,只是 StringBuilder是线程不安全的
2.内部类的定义格式
public class 外部类{
3.内部类在编译完成后也会产生.class文件,而文件名称则是”外部类名称$内部类名称.class”
1 可以方便地访问外部类的私有属性
2 减少了类文件编译后的产生的字节码文件的大小
缺点:使程序结构不清楚
成员内部类持有外部类的引用
在文件管理方面,成员内部类在编译完成后也会产生.class文件,而文件名称则是”外部类名称$内部类名称.class”
外部实例化成员内部类的格式:
外部类.内部类 内部类对象=外部类实例.new 内部类();
package inner.member;
public class Outer {
private String name="中国人";
// 成员内部类
class MemberInner{
public void method(){
System.out.println("内部类可以访问外部类的私有属性:"+name);
}
}
public MemberInner getMemberInstance(){
return new MemberInner();
}
}
//test类
package inner.member;
public class TestMember {
public static void main(String[] args) {
Outer out=new Outer();
out.getMemberInstance().method(); // 通过在外部类中的成员方法中获取内部类对象
// 外部类.内部类 内部类对象=外部类实例.new 内部类();
Outer.MemberInner in=out.new MemberInner();
in.method();
}
}
静态内部类不会持有外部类的引用,创建时可以不用创建外部类对象
静态内部类可以访问外部的静态变量,如果访问外部类的非static成员变量必须通过外部类的实例访问
外部实例化静态内部类对象的格式:
外部类.内部类 内部类对象= new 外部类.内部类();
package inner.staticdemo;
public class Outer {
private String name="中国";
private static int population=14;
static class StaticInner{
public void method(){
System.out.println("静态内部类直接访问外部类的static属性:"+population);
Outer out=new Outer();
System.out.println("在静态内部类中通过外部类对象访问非static属性:"+out.name);
}
}
public static StaticInner getStaticInner(){
return new StaticInner();
}
}
//test类
package inner.staticdemo;
public class TestStaticInner {
public static void main(String[] args) {
Outer.getStaticInner().method();
System.out.println("************************");
Outer.StaticInner in=new Outer.StaticInner();
in.method();
}
}
局域内部类是定义在一个方法中的内嵌类,所以类的作用范围仅限于该方法中,而类生成的对象也只能在该方法中使用。
局域内部类不能包含静态成员
特别注意:局域内部类要想访问包含其的方法中的参数,则方法中的参数前必须加上final关键字(JDK<=1.7)。
4.匿名内部类
没有名字的内部类,这是java为了方便我们编写程序而设计的一个机制,因为有时候有的内部类只需要创建一个它的对象就可以了,以后再不会用到这个类,这时候使用匿名内部类就比较合适
eg:
package inner.anonymity;
public interface Jumping {
public void jump();
}
//test类
package inner.anonymity;
public class TestAnonumity {
public static void main(String[] args) {
Jumping j=new Jumping(){
@Override
public void jump() {
System.out.println("某人跳楼了...");
}
};
j.jump();
}