概述
运算符
计算机工作的原理就是计算,否则也不会叫计算机,所以每种语言都对计算进行了很好的支持,Java中有丰富的运算符来支撑计算。Java中运算符大致分为以下几类:
类型 | 符号 |
---|---|
1.算术运算符 | +、-、*、/、%、++、– |
2.关系运算符 | ==、!=、>、<、>=、<= |
3.位运算符 | &、或、^、~ 1、<< 2、>> 3、>>> 4 作用于整数和char类型上 |
4.逻辑运算符 | &&、或或、! |
5.赋值运算符 | =和算术运算符的结合 |
6.其他运算符 | 条件运算符?:、instanceof |
优先级:
类别 | 操作符 |
---|---|
- | ()、. 点(调用) |
一元 | !、~、++、-- 5 |
乘除 | *、/、% |
加减 | +、- |
位移 | << 、>>、>>> (arr1.length+arr1.length>>1) |
关系 | ==、!=、>、<、>=、<= |
与 | & |
异 | ^ |
或 | 或 6 |
逻辑 | &&、 |
三目 | ?: |
赋值 | =、乘除、加减、位移和赋值混合 |
动态数组
数组的元素个数是固定的,如果数组满了可不可以自动让数组扩容呢?通过程序来处理是可以达到的,但是此数组其实已经不是原来的数组了。
假如有一个班级对象clazz,这有一个方法addStudent,这个方法的参数是Student对象,当我们调用addStudent方法添加学生时,在clazz对象中有一个students数组来存放学生对象,但是当数组满了后,就要创建一个新的数组,这个数组要比原数组大,并把原数组中的学生对象转移到新数组中,并把原数组释放了,这样就成了动态数组,只要调用addStudent者愿意,它就可以一直给clazz添加学生。开发中当数组满时,创建的新数组大小不会是比原数组多一个元素,这样会在后续加入学生时,每次都创建新数组,转移数组元素,释放原数组,效率太低,往往创建的新数组大小是原数组的1.5倍大小。
int oldCapacity = elementData.length;
// (oldCapacity >> 1)相当于除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 转移数组元素, elementData原数组 newCapacity新数组长度 copyOf返回新数组
elementData = Arrays.copyOf(elementData, newCapacity);
算法案例
此部分的案例主要是对数组的排序算法,但是如果没有特殊要求,最好使用Arrays.sort方法,它的效率还是不错的,sort方法需要传入一个参数,数组对象,并且数组中的元素要实现排序接口Comparable,否则,sort方法并不知道如何对数组中元素排序。还有另一个办法,允许数组中元素可以不实现Comparable接口,但调用sort方法时不仅要传入数组对象,还需要额外传入一个自定义的排序器。无论如何,Arrays的sort方法自身并不知道如何对数组元素进行排序,它要么依赖数组元素自己排序,要么依赖外部的排序器。对于Java中的八大数据类型和String都已经实现了排序接口,所以对于这些数组还是挺方便的。如果是我们自定义的类如Student,就要自己设计如何排序了。
冒泡
冒泡排序是最基本的排序,要求必须掌握的排序算法。
冒泡排序是最简单的,但是效率最低的排序算法,它的原理是:
1.拿数组第1个元素和它后面的每一个元素进行比较,如果小于等于后面元素则不做交换,如果大于后面的元素则交换一次,直到最后一个元素,结果是数组最后一个元素是数组中最大的元素。
2.和第1次一样,一一比较,区别是已经知道最后一个元素是最大的元素,所以此次不用比较,少比较一次。
3.略
//需要进行arr.length趟比较
for(int i = 0 ;i<arr.length-1;i++){
//第i趟比较
for(int j = 0 ;j<arr.length-i-1;j++){
//开始进行比较,如果arr[j]比arr[j+1]的值大,那就交换位置
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
排序的算法还有:选择排序,插入排序,快速排序等。
重载
什么是重载呢?在一个类中,当两个方法的方法名相同,参数列表不同时,这两个方法就构成了重载。重载的必要条件是,方法名相同,参数列表不同,对于方法的返回值不做要求。
为什么需要重载呢?方法名相同,说明它们处理的业务应该是类似的,不同的是处理的对象略有不同,此时就需要重载,如果不重载也可以,就是给方法起不同的名称,但是,既然它们处理的事情类似,就不如给它们起同样的名称,只要参数列表的个数,顺序或类型不相同就可以,Java是允许这么做的。
重载是一个非常常见的现象,在Java的源码中比比皆是,有机会看源码时注意观察这个现象。
下面是简单的代码
public void print(String msg){}
public void print(int num){}
注意:参数列表不同,不是说参数名不同,编译器只看参数类型不看变量的名称。
print(String str1){}和print(String str2){}并不构成重载
继承和多态
继承可以使子类方便拥有父类的成员,使代码可以复用,使代码更好地维护。继承的关键字是extends
此处的知识较多,且经常出现在面试中。
继承不建议层次太多,否则类结构过于复杂。
继承打破了封装,如果一个类中的成员不可以被别的类调用时,但如果继承了该类,则可以访问本不能访问的成员。(在包和权限中再说明 )
对象初始化
class A{}
class B extends A{}
B b = new B(); 此时在内存中会先创建B父类A的对象new A()然后再new B(),并且在b对象中包含一个a对象。如果A还有父类的话,会先创建A的父类对象,依次类推。
方法重写
子类可以继承父类的方法,当子类也有同名的方法时,就会覆盖父类的方法,这种现象叫方法的重写。重写是有限制的:子类中方法的签名必须要和父类中的方法签名完全相同,即方法名、返回类型和方法参数列表必须相同,访问修饰符不能小于父类的。子类中重写的方法所能抛出的异常不能强于父类的(强是指异常的数量和类型不,异常后续再说明)
class A{
protected void m1(String msg){}
}
class B extends A{
/**
* 访问修饰符可以是protected也可以是public,剩下的必须和A中的m1方法一样,除了参数名可以不同,方法体中的代码可以不同
*/
public void m1(String msg){}
}
继承和属性
父类的属性如果不是private是可以继承过来的,但是无法被子类覆盖。
class A{
protected int id=1;
private String name = "father";
}
class B extends A{
protected int id=2;
}
B b = new B();
sout(b.id)结果是2。
sout(b.name)结果是"father" 。
这个不是覆盖,是隐藏了父类的属性,它的原理是:当通过b对象调用属性id时,在b对象内找到了id就返回,不再进一步查找,当通过b调用name时,因为b中没有,就往上找,找到了它包含的父类的属性name,返回该属性。
多态
由父类声明的变量指向它的子类,当指向不同的子类时,因为子类之间的差异,从而使该变量表现出不同的特性的现象叫做多态。
同一个行为(方法)具有不同表现形式的能力。
class A{
protected int id = 1;
protected void say(){
sout("hello A");
}
class B extends A{
protected int id = 2;
protected void say(){
sout("hello B");
}
class C extends A{
protected int id = 3;
protected void say(){
sout("hello C");
}
A a = new B();
sout(a.id)结果是1,因为属性是无法覆盖的,实际上谁声明的变量,就是谁的属性。
a.say() 结果是hello B,因为say方法被重写了,就是覆盖了。
a = new C();
a.say() 结果是hello C,这就是多态,a的表现是多种形态的。
静态
static 类文件加载到JVM中时就初始化,只和类有关,和对象无关,可继承,不可覆盖。
静态方法不能访问非静态的成员。
抽象和接口
抽象是不具体,在父类中往往有些方法无法具体描述细节,就把该方法定义成抽象方法,具体如何实现细节由其子类的重写方法来实现。
没有实现细节的方法叫抽象方法,没有方法体,方法声明需要加修饰符abstract。
有了抽象方法的类就必须定义成抽象类,abstract class xxxx{} ,但是抽象类可以没有抽象方法。抽象类存在的意义就是为了继承,如果没有继承,抽象类将没有任何意义,因为抽象类无法实例化,无法创建对象。假设抽象类可以创建对象,可以实例化,那么这个对象调用抽象方法将会是矛盾的,因为抽象方法什么也不能做。
abstract class Father{
public abstract void say();
}
class Son extends Father{
public void say(){
sout("say hello");
}
}
利用抽象类和抽象方法可以完成一些设计模式,如模板模式:
public abstract class Car {
protected void assemble(){
engine();
tyre();
}
protected abstract void engine();
protected abstract void tyre();
}
class Benchi extends Car{
@Override
protected void engine() {
sout("安装奔驰引擎");
}
@Override
protected void tyre() {
sout("安装奔驰轮胎");
}
}
class Test{
public static void main(String[] args) {
Car benchi = new Benchi();
benchi.assemble();
}
}
super
补充super,super意思是超级的,在这儿主要是和父类有关。super要关注两个用法:
1.调用父类的构造方法
如果父类有默认构造方法,则可以不用super,但是,如果父类只有有参的构造方法,此时,在创建子类对象时,在子类的
构造方法中就要显示的调用super构造方法。
2.在子类方法中调用父类已经被重写的方法
public class Animal {
protected String name = "Animal";
protected void say(){
System.out.println("Animal say...");
}
protected void test(){
this.say();// ?1
System.out.println(this.name);// ?2
}
public static void main(String[] args) {
Animal animal = new Cat();
animal.test();
}
}
class Cat extends Animal{
protected String name = "Cat";
@Override
public void say(){
System.out.println("miao miao...");
}
@Override
public void test(){
this.say();// ?3
super.say();// ?4
}
}
以上代码是一段父子继承的代码,其中有关于继承时属性和方法如何处理的问题。这儿要说一个多态的问题,
Animal animal = new Cat();
此时的animal虽然指向了Cat的实例,但是它同时也是Animal的身份,所以,animal是无法调用Animal中不存在的方法和属性。它能调用的属性和方法要么是Animal自身的,要么是被Cat覆盖的(只有方法才能覆盖)。
假设Cat中没有对test方法重写(可以注释掉此方法),这时调用test是Animal中的test方法,test方法中两个问题各输出什么?
protected void test(){
this.say();// ?1
System.out.println(this.name);// ?2
}
?1:这儿的this.say()虽然写在父类中,父类的say方法已经被覆盖了,this.say()结果就是“miao miao…"
?2:这儿this.name得到的是animal,因为属性是无法覆盖的。
再来说一下,把子类的test方法放开,使其覆盖了父类的test方法,此时animal.test()调用的是Cat的test方法
@Override
public void test(){
this.say();// ?3
super.say();// ?4
}
?3:调用的当然是cat的say方法“miao miao…”
?4:因为是super.say(),此时调用的是Animal中的say方法,super可以调用被子类覆盖的方法,super在此处代表了父类。
instanceof和强转
instanceof运算符是特殊的运算符,可以判断某个对象是否某个类的实例。
如果B是A的子类,则:
A obj = new B(); 赋值号= 左边是大类型,基本类型,右边是子类型,则可以自然转换,如:Chinese obj = new Shanxi();
if (obj instanceof B) 则B b = (B)obj;当检查到obj实际身份是B时,可以还原其身份到B类型,但此时obj的外在身份依然是A,所以转换时还是要加括号进行强转,强转的前提是类型正确,比如C也是A的子类,上面的obj就不能强转成C,会报类型转换错误的异常。
接口
接口是定义类的行为规范,只描述需要做什么,并不关心如何实现,实现的细节由接口的实现类来完成。Java8版本前,接口是没有普通方法的,全是抽象方法,Java8后,接口中也可以有普通方法,而不仅是抽象方法,只是普通方法要加default关键字,称此类方法为接口的默认方法。
接口的关键字是interface,它的实现类需要 implements关键字。
public interface A{
// public static final 可以省略,接口中定义的变量都是常量
public static final int NUM = 1;
// 接口中的方法默认是public abstract,所以这两个关键字可以省略
public abstract void m1();
public default void m2(){}
}
接口和抽象类的区别:
1.接口中的变量都是常量
2.接口中只有default方法和抽象方法
3.抽象类中可以有普通方法
4.抽象类有构造方法
包和权限
package,可以有效管理类,比如把功能相同的类放在一个包下,如:service包下放所有的业务类StudentService.java,ClazzService.java等
或者按模块管理,如:student包下放StudentBean.java,StudentService.java,StudentDao.java
目前按照习惯,项目代码按照功能分为三层结构:控制层,业务层,持久层。这三个层通常就是三个包:servlet,service,dao
随手做:建工程,建包
java.lang包,java.util包,java.io包,java.sql包
内部类
内部类:定义在一个类内部的类。
它可以在定义在一个类中的属性的区域,或方法中,或一个代码块中(if,for)
内部类可以有名称,可以没有名称(匿名内部类)
匿名内部类多用于抽象类或接口的实例化(抽象类和接口本身是不能创建对象的,不能实例的,但是可以通过匿名内部类间接实现)
内部类的优点:它可以具有类的功能,但是只限于当前类使用,不对外,否则就定义成一个外部的类。
内部类可以绕开Java的类的单继承,如果外部类继承了一个类,而此时还想继承另一个类,就让内部类来继承它。
两点:别人不用,自己用(所以是内部类);内部类可以定义成方法来使用,但方法有局限性,当一个方法完成不了时,需要定义成一个类,方法升级为内部类后,功能增强。可以组织代码,使的代码结构清晰。
分类 :
1.成员内部类
2.方法内部类
3.块内部类
4.匿名内部类
public class Outer {
int id = 1;
/**
* 成员内部类
*/
class Inner1{
public void say(){
System.out.println("Inner1.say");
}
}
public static void main(String[] args) {
Outer outer = new Outer();
// 访问类的成员
System.out.println(outer.id);
// 创建内部类的实例,需要外部类的实例
Outer.Inner1 inner1 = new Outer().new Inner1();
inner1.say();
}
}
/**
* 静态成员内部类
*/
static class Inner2{
public void say(){
System.out.println("Inner2.say");
}
}
main:
Outer.Inner2 inner2 = new Outer.Inner2();
inner2.say();
内部类可以访问外部类的数据(注意静态的使用,如果内部类是静态的,则情况复杂)
静态内部类只能访问外部类的静态数据
package c313.p4;
/**
* copyright(c)2021 YCKJ.ALL rights Reserved
* <p>
* 描述:
*
* @author ychs
* @version 1.0
* @date 2021/3/13
*/
public class Outer {
int id = 1;
/**
* 成员内部类
*/
class Inner1{
public void say(){
System.out.println("Inner1.say");
System.out.println("Outer.id=" + id);
}
}
/**
* 静态成员内部类
*/
static class Inner2{
public void say(){
System.out.println("Inner2.say");
//System.out.println("Outer.id=" + id);
}
}
public void work(){
/**
* 方法内部类,只能在方法内部使用
*/
class Inner1{
public Inner1(){
System.out.println("method work Inner1 created");
}
void work(){
System.out.println("method Inner1.work");
}
}
Inner1 inner1 = new Inner1();
inner1.work();
}
public static void main(String[] args) {
Outer outer = new Outer();
// 访问类的成员
System.out.println(outer.id);
// 创建内部类的实例,需要外部类的实例
Outer.Inner1 inner1 = new Outer().new Inner1();
inner1.say();
Outer.Inner2 inner2 = new Outer.Inner2();
inner2.say();
outer.work();
}
}
package c313.p4;
/**
* copyright(c)2021 YCKJ.ALL rights Reserved
* <p>
* 描述:
*
* @author ychs
* @version 1.0
* @date 2021/3/13
*/
public class Outer {
int id = 1;
/**
* 成员内部类
*/
class Inner1{
public void say(){
System.out.println("Inner1.say");
System.out.println("Outer.id=" + id);
}
}
/**
* 静态成员内部类
*/
static class Inner2{
public void say(){
System.out.println("Inner2.say");
//System.out.println("Outer.id=" + id);
}
}
public void work(){
/**
* 方法内部类,只能在方法内部使用
*/
class Inner1{
public Inner1(){
System.out.println("method work Inner1 created");
}
void work(){
System.out.println("method Inner1.work");
}
}
Inner1 inner1 = new Inner1();
inner1.work();
}
public void work2(){
if(id >0 ){
/**
* 块内部类
*/
class Inner1{
public Inner1(){
System.out.println("if block Inner1 created");
}
}
Inner1 inner1 = new Inner1();
}
}
/**
* 匿名内部类,一个没有名称的实现了IAnimal接口的类
*/
IAnimal animal = new IAnimal() {
@Override
public void say() {
System.out.println("匿名内部类的say方法");
}
};
public static void main(String[] args) {
Outer outer = new Outer();
// 访问类的成员
System.out.println(outer.id);
// 创建内部类的实例,需要外部类的实例
Outer.Inner1 inner1 = new Outer().new Inner1();
inner1.say();
Outer.Inner2 inner2 = new Outer.Inner2();
inner2.say();
outer.work();
outer.work2();
// animal是Outer类的一个成员,这个成员是匿名内部的实例
outer.animal.say();
}
}
以上是所有内部类的案例代码。
附录:
javadoc
javadoc -encoding utf-8 -charset utf-8 A.java
↩︎// 按位取反 int num = ~3; // 3原码:0000,0011 // 取反后:1111,1100(这是补码,换成原码) // 补码-1->反码(取反)->原码 // 1111,1100-1->1111,1011(取反)->1000,0100(-4) System.out.println(num); // 1000,0101(原)->1111,1010(反)->1111,1011(加1)补码 num = -5; // 对-5的补码取反 // 1111,1011->0000,0100(4) num = ~num; System.out.println(num);
↩︎// 相当于2乘以2的3次方 // 2:0000,0010.最后一位后有一个原点作为移位的基点 // 左移:把0000,0010.向左移,右补零0001,0000. int num = 2<<3; System.out.println(num); // 先算-2的原码1000,0010,反码1111,1101,补码1111,1110 // 1111,1110左移3位1111,0000(补) // 1111,0000(补)->1110,1111(-1得反码)->1001,0000(取反得原码-16) num = -2<<3; System.out.println(num);
↩︎// 0001,0000.->0000,0010把数字向右移3位高位补0 int num = 16 >> 3; System.out.println(num); // 原码:1001,0000->1110,1111(反码)->1111,0000(补码) // 1111,0000右移3位,高位补1。 // 补:1111,1110->1111,1101(减1得反码)->1000,0010(原码-2) num = -16 >> 3; System.out.println(num);
↩︎// 正数的无符号右移和有符号右移没有区别,负数不同 // 原码:1001,0000->1110,1111(反码)->1111,0000(补码) // 1111,0000右移3位,高位补0 // 1111,1111,1111,1111,1111,1111,1111,0000 // 0001,1111,1111,1111,1111,1111,1111,1110(536870910) int num = -16 >>> 3; System.out.println(num);
↩︎byte n1 = 5; byte n2 = 6; // 5*6=30 说明变量在同一个表达式中在变化值 int n3 = n1++ * n1++; System.out.println(n3); // 7 * 8=56 n3 = ++n2 * ++n2; System.out.println(n3); //------------------------------------------------------------------------------------------ byte n1 = 5; byte n2 = 6; // 虽然+比*运算符低但++要先算 int n3 = n1++ + n1++ * n1++; System.out.println(n3); //------------------------------------------------------------------------------------------- byte b1 = 5; byte b2 = 6; // 5|6^7&4*7*5 //010|0110^0111&240(11110000)=0111 int num = b1++ | b2-- ^ ++b1 & --b2 * b1-- * ++b2; System.out.println(num);
↩︎byte b1 = 1; byte b2 = 2; byte b3 = 3; byte b4 = 4; // 先3&4 结果 ^ 2 结果 | 1=3 int num = b1 | b2 ^ b3 & b4; System.out.println(num); //先2&1 结果^3 结果|4 num = b4 | b3 ^ b2 & b1; System.out.println(num);
最后
以上就是忧伤外套为你收集整理的Java二运算符动态数组算法案例重载继承和多态静态抽象和接口superinstanceof和强转包和权限内部类附录:的全部内容,希望文章能够帮你解决Java二运算符动态数组算法案例重载继承和多态静态抽象和接口superinstanceof和强转包和权限内部类附录:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复