中级java进阶

java基础类型篇

在java中switch作用类型

在java中,只能作用int基本类型,因为short,char,byte都可以隐士转换为int类型,所以这些以及这些类型的包装类型也是可以的,但是long,string不能被隐士转换为int类型,所以他们不能被作用于switch语句中

Char型变量中能不能存储一个中文汉字

Char型变量用来存储unicode编码字符的,unicode编码字符集中包含了汉字,所以char型变量可以存储汉字,但是如果某个特殊的汉字没有被包含到unicode编码字符集中,那么这个char型变量就不能存储这个汉字,unicode编码占两个字节,所以char类型的变量也就占2个字节。

使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变

使用final关键字修饰一个变量时,是指的引用变量不能变,引用变量指向的对象中的内容还是可以发生改变的,例如有一行代码:final StringBuffer a =new StringBuffer(“liuxiang”);   如果执行以下这行代码将报告编译错误 : a= new StringBuffer(“”);   但是执行如下代码就将编译通过: a.append(“ciji”);

“==”和equals方法到底有什么区别

==操作符是专门用来比较两个变量的值是否相等,也就是用来比较变量所对应的内存中存储的数值是否相等,要比较两个基本数据类型或者引用变量是否相等,就只能用”==”操作符了。如果一个变量指向的数据是对象类型的,那么这就设计到了两块内存,对象本身占用一块内存(堆),变量也要占用一块内存。例如:Object a = new Object();变量a占用一块内存,newObject()也占用一块内存。此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,就要看这两个变量所对应的内存中的数值是否相等,这时候就要”==”操作符进行比较。Equals方法是用于比较两个独立对象的内容是否相同,它比较的两个对象是独立的。例如如下代码: String a = new String(“foo”); String b=new String(“foo”);  两条new语句创建了两个对象,然后a,b两个变量指向了其中一个对象,这是两个不同的对象,他们的首地址是不同的,既a,b中存储的数值是不同的,所以a==b将返回false。而这两个对象中的内容是相同的,所以a.equals(“b”)将返回true

因为在编写程序中的时候会需要考虑代码编写后运行的效率问题,所以堆栈的使用概念至关重要:用如下图示来加深印象。(注:内存中共分为5大块)

 

Stack(栈)

Heap(堆)

 


                                                             

1.         Int a

2.         Int b

3.         Int c

4.         Final 局部变量

5.         …

New liuxiang()

New yaoming()

New taiciji()

Final 局部变量

“china”

9,11,44

常量池

Public void..

Private String..

Code

Static …..

Static ……

静态域

Young年轻态

Old 老年态

Parement保存

反射对象

堆又分为3个区

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


目前本人已知的数据结构只有栈,本着后进先出原则执行。而堆目前不清楚

文字详解:

[编辑本段]堆和栈的区别

  一、预备知识—程序的内存分配

  一个由C/C++编译的程序占用的内存分为以下几个部分

  1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

  2、堆区(heap)— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

  3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区

域。程序结束后由系统释放。

4、文字常量区常量字符串就是放在这里的,程序结束后由系统释放

  5、程序代码区— 存放函数体的二进制代码。

  二、例子程序

  这是一个前辈写的,非常详细

  //main.cpp

  int a = 0; 全局初始化区

  char *p1; 全局未初始化区

  main()

  {

  int b; 栈

  char s[] = "abc"; 栈

  char *p2; 栈

  char *p3 = "123456"; 123456\0在常量区,p3在栈上。

  static int c =0;全局(静态)初始化区

  p1 = (char *)malloc(10);

  p2 = (char *)malloc(20);

  }

  分配得来得10和20字节的区域就在堆区。

  strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。

[编辑本段]堆和栈的理论知识

  

1.申请方式

  stack:

  由系统自动分配。例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间

  heap:

  需要程序员自己申请,并指明大小,在c中malloc函数

  如p1 = (char *)malloc(10);

  在C++中用new运算符

  如p2 = new char[20];//(char *)malloc(10);

  但是注意p1、p2本身是在栈中的。

  

2.申请后系统的响应

  

  栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

  堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,

并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结

点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

  

3.申请大小的限制

 

  

  栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是

1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

  堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计

算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

  

4.申请效率的比较

  

  栈由系统自动分配,速度较快。但程序员是无法控制的。

  堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

  另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活

  

5.堆和栈中的存储内容

 

  

  栈:在函数调用时,第一个进栈的是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入

栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

  当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

  堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

  

6.存取效率的比较

 

  char s1[] = "aaaaaaaaaaaaaaa";

  char *s2 = "bbbbbbbbbbbbbbbbb";

  aaaaaaaaaaa是在运行时刻赋值的;

  而bbbbbbbbbbb是在编译时就确定的;

  但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

  比如:

  #include

  void main()

  {

  char a = 1;

  char c[] = "1234567890";

  char *p ="1234567890";

  a = c[1];

  a = p[1];

  return;

  }

  对应的汇编代码

  10: a = c[1];

  00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

  0040106A 88 4D FC mov byte ptr [ebp-4],cl

  11: a = p[1];

  0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]

  00401070 8A 42 01 mov al,byte ptr [edx+1]

  00401073 88 45 FC mov byte ptr [ebp-4],al

  第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。

  

7.小结:

  堆和栈的区别可以用如下的比喻来看出:

  使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

  使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

[编辑本段]堆和栈的区别主要分:

  操作系统方面的堆和栈,如上面说的那些,不多说了。

  还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的

性质的数学或数据结构。

  虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。

[编辑本段]补充

  堆栈是一种存储部件,即数据的写入跟读出不需要提供地址,而是根据写入的顺序决定读出的顺序

静态变量和实际变量的区别

实例变量属于某个对象的属性,必须创建了实例对象,实例变量才会被分配空间,才能使用这个实例变量。而静态变量不属于某个实例对象,而是属于类,所以也被称为类变量,只要程序加载了类得字节码,不用创建实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以使用,而静态变量则可以使用类名来引用。

构造器constructor是否可以被override

构造器因为不能被继承,所以不能被重写,但可以重载。

String s =”a”+”b”+”c”;这句代码公创建了多少个对象

只创建了一个对象,因为javac对字符串常量直接相加表达式进行了优化,不必在运行期再做加法处理,而是在编译时去掉中间的加号,直接编译成一个常量相连的结果。

从java5开始,有线程池创建线程的方法

ExecutorService pool= Executors.newFixedThreadPool(3)

for(inti=0;i<10;i++)

{

 pool.execute(new Runable(){public voidrun(){}});

}

Executors.newCachedThreadPool().execute(newRunable(){public void run(){}});

Executors.newSingleThreadExecutor().execute(newRunable(){public void run(){}});

Sleep()和wait()有什么区别

Sleep()方法是不会主动释放锁的(条件是当前线程进入了同步锁),也就是说如果当前线程主动让出了cpu,但是其他被同步锁挡住了的线程也无法执行。而wait()方法是主动释放锁,进入等待此对象的对象锁池,只有针对此对象发出notify()方法后本线程才进入对象锁定池准备获得对象锁进入运行状态。但是notify并不是释放锁,而是告诉其他wait()的线程可以进入获得锁的队列了,必须当notify的那个线程将代码全部执行完毕之后,wait()的线程才会有获得锁的机会。

Sleep()方法还有一种情况是:如果当前线程没有进入同步锁的话,则调用该方法后,当前线程会让出cpu,cpu去执行其他线程,在sleep执行的时间过后,cup才会回到这个线程继续执行下去。

同步和异步的区别

如果数据在线程间共享,例如正在写的数据有可能被另一个线程独到,或者正在读的数据有可能已经被重写过了,那么这种情况就需要进行数据的同步存取。

异步就是当应用程序在对象上调用了一个需要花费很长时间才能执行完的方法的时候,但又不希望让程序等待方法的返回,这时候就应该用到异步编程,该方法的应用会提升程序的效率。

简述synchronizedjava.util.concurrent.locks.Lock的异同

Lock有比synchronization更精确的线程语意和更好的性能,因为synchronization会自动释放锁,而lock则需要程序员手动释放锁。

ArrayList和Vector区别

这两个类都实现了List接口,他们都是有序集合,既存储在这两个集合中的元素位置都是有顺序的,相当于一种动态数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是与hashset之类的集合最大的不同之处。Hashset之类的集合没有按照索引号去检索其中的元素,也不允许有重复的元素。

就同步性而言:vector是线程安全的,也就是说它的方法之间是线程同步的,而arraylist是不同步的,如果只有一个线程访问到集合,那最好使用arraylist,因为它不考虑线程安全方面的问题,效率会高些。但是如果要有多个线程访问该集合的话,那就使用vector。因为如果这种情况去使用vector的话,我们自己在编写代码的时候就不需要再去考虑线程安全的代码了,因为vector集合类已经将这些问题都处理好了。

就数据增长而言,两种集合都有一个初始的容量大小,当存储到这些集合的元素超过初始值时,就会增加他们的存储空间。但是这时候就有差别出现了,每次需要增加存储空间的时候,vector增长原来的两倍,arraylist是增长原来的1.5倍。这两种集合都可以设置初始空间大小,但是我觉得没有什么意义。

List接口继承了collection接口。

Hashmap和hashtable的区别

Hashmap是hashtable的轻量级实现,(非线程安全的实现),说到这顺便再提一下hashmap和arraylist。这两种集合都是后来才出的,换句话说后来设计的这两种类都不是线程安全的,但是之前一开始就有了的vector和hashtable是线程安全的。

这两种类都实现了map接口。Hashmap允许将一个null作为一个entry的key或者value。而hashtable继承自陈旧的Dictionary类,并且不能讲null作为entrykey或者value

List.map.set这三种集合的区别

List和set都具有共同的父类,那就是collection类。但是set不允许有重复元素,而list可以存取重复的元素。

去掉vector集合中的重复元素

Hashset set = new hashset(vector);

Collection和collections的区别

Collection是集合类的上级接口,collections是针对集合类的一个帮助类,它提供了一系列静态方法对各种集合的搜索,排序,线程安全化等操作。

两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

对。

如果对象要保存在HashSetHashMap中,它们的equals相等,那么,它们的hashcode值就必须相等。

如果不是要保存在HashSetHashMap,则与hashcode没有什么关系了,这时候hashcode不等是可以的,例如arrayList存储的对象就不用实现hashcode,当然,我们没有理由不实现,通常都会去实现的。

什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用。

web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输或通过rmi等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现Serializable接口。

Heap和stack有什么区别

栈内存主要是指当程序进入一个方法时,会为这个方法单独分配一个私属存储空间,用于存储这个方法里的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也随之释放。方法中的局部变量使用final修饰后,放在堆中!

Java中内存泄露情况

通俗来讲:内存泄露就是程序员创建了一个对象,以后一直不在使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收,这时候就出现了内存泄露的情况。比如:缓存系统。我们加载了一个对象放在缓存中(例如放在了一个全局map对象中),然后一直不再使用它,但是这个对象却一直被缓存引用,却不再使用。

局部变量小结

局部变量不能用访问修饰符,但是可以用final修饰

publicclass Something {
   public int addOne(final int x) {
       return ++x;
   }
}
上述这段代码的问题是,传过来的变量已经被final修饰过了,所以不能在下面的方法体中不能被修改。

Hashcode详解

说到hash码,再重谈一次set集合和list集合的区别,看上去经常用的东西,其实如果要说的话,还真有可能一时半会想不起来作用。List集合是有序的,所以取元素的时候可以直接去找集合里的第几个元素,但是set集合是无序的,所以要想遍历set集合的话就必须用iterator去逐个迭代。

下面再说说哈希码。主要针对set集合来说明这个问题,set集合是不允许有重复元素的,具体实现的原理就是当每放一个元素的时候就会用object的equals方法来匹配。但是如果集合里的元素过多的时候再逐一去匹配的话,那效率会是非常低的,所以java采用了hashcode来优化这种存储方案。当集合要添加新的元素的时候,先调用这个元素的hashcode方法,就能定位到它应该放置的物理地址上,不用再进行任何比较了,如果这个位置上有元素了,就调用这个元素的equals方法与新来的元素进行比较,相同的话就不存了,不相同的话就散列到其他位置上,这样一来就只需要调用一次或者两次equals方法就哦了

Java对equals方法和hashcode方法是这样定义的,如果两个对象相同,那么他们的hashcode就相同,但是如果只是hashcode相同的话,那他们不一定相同。

Spring的理解

1.Spring实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式),这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationContextSpring相当于一个大的工厂类,在其配置文件中通过<bean>元素配置用于创建实例对象的类名和实例对象的属性。

2. Spring提供了对IOC良好支持,IOC是一种编程思想,是一种架构艺术,利用这种思想可以很好地实现模块之间的解耦。IOC也称为DIDepency Injection),什么叫依赖注入呢?

譬如,ClassProgrammer

{

         Computer computer = null;

         public void code()

         {

                   //Computer computer = newIBMComputer();

                   //Computer computer =beanfacotry.getComputer();

                   computer.write();

         }

         public void setComputer(Computercomputer)

         {

                   this.computer = computer;

         }

}
另外两种方式都由依赖,第一个直接依赖于目标类,第二个把依赖转移到工厂上,第三个彻底与目标和工厂解耦了。在spring的配置文件中配置片段如下:

<bean id=”computer”class=”cn.itcast.interview.Computer”>

</bean>

 

<bean id=”programmer”class=”cn.itcast.interview.Programmer”>

         <property name=”computer”  ref=”computer”></property>

</bean>

3. Spring提供了对AOP技术的良好封装, AOP称为面向切面编程,就是系统中有很多各不相干的类的方法,在这些众多方法中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为AOP。实现AOP功能采用的是代理技术,客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有相同的方法声明,有两种方式可以实现相同的方法声明,一是实现相同的接口,二是作为目标的子类在,JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,如果要为某个类生成子类,则可以用CGLI B。在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,显然要创建出代理对象,至少需要目标类和Advice类。spring提供了这种支持,只需要在spring配置文件中配置这两个元素即可实现代理和aop功能,例如,

<bean id=”proxy”type=”org.spring.framework.aop.ProxyBeanFactory”>

         <property name=”target” ref=””></property>

         <property name=”advisor” ref=””></property>

 

</bean>

 

浅谈java反射机制

Java的反射机制无非就是动态绑定类,再运行时可以调用和创建别的类的方法和属性等。。比如说要读一个xml文件,但当执行类或者方法改变了的话,那就需要修改源码了,而用了反射机制的话,就只需要更改配置文件即可,说到这也就想起来了spring的aop。Ioc等框架,修改了配置文件的耦合代码,就可以动态在运行时绑定要耦合的其他类或者对象的属性和方法。

9、递归算法题2

第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大?

package cn.itcast;

 

import java.util.Date;

 

public class A1 {

 

    public static void main(String [] args)

    {

       System.out.println(computeAge(8));

    }

   

    public static int computeAge(int n)

    {

       if(n==1) return 10;

       return computeAge(n-1) +2;

    }

}

 

    public static void toBinary(intn,StringBuffer result)

    {

 

       if(n/2 != 0)

           toBinary(n/2,result);

       result.append(n%2);     

    }

 

1Tomcat的优化经验

:去掉对web.xml的监视,把jsp提前编辑成Servlet

有富余物理内存的情况,加大tomcat使用的jvm的内存

. 数据库部分

1、用两种方式根据部门号从高到低,工资从低到高列出每个员工的信息。

employee:

     eid,ename,salary,deptid;

 select * from employee order by deptiddesc,salary

 

 

2、列出各个部门中工资高于本部门的平均工资的员工数和部门号,并按部门号排序

创建表:

       mysql> create table employee921(idint primary key auto_increment,name varchar(5

0),salarybigint,deptid int);

 

插入实验数据:

mysql> insert intoemployee921 values(null,'zs',1000,1),(null,'ls',1100,1),(null

,'ww',1100,1),(null,'zl',900,1),(null,'zl',1000,2), (null,'zl',900,2) ,(null,'z

l',1000,2) ,(null,'zl',1100,2);

 

编写sql语句:

 

()selectavg(salary) from employee921 group by deptid;

()mysql> selectemployee921.id,employee921.name,employee921.salary,employee921.dep

tid tid from  employee921 where salary > (selectavg(salary) from employee921 where deptid = tid);

   效率低的一个语句,仅供学习参考使用(在group by之后不能使用where,只能使用having,在group by之前可以使用where,即表示对过滤后的结果分组):

mysql> selectemployee921.id,employee921.name,employee921.salary,employee921.dep

tid tid from  employee921 where salary > (selectavg(salary) from employee921 group by deptid having deptid = tid);

()select count(*),tid

         from (

                   selectemployee921.id,employee921.name,employee921.salary,employee921.deptid tid

                   from       employee921

                   where salary >

                          (select avg(salary) fromemployee921 where  deptid = tid)

         ) as t

         group by tid ;

 

另外一种方式:关联查询

selecta.ename,a.salary,a.deptid

 from emp a,

    (select deptd,avg(salary) avgsal from empgroup by deptid ) b

 where a.deptid=b.deptid anda.salary>b.avgsal;

3、存储过程与触发器必须讲,经常被面试到?

create procedureinsert_Student (_name varchar(50),_age int ,out _id int)

begin

         insert into studentvalue(null,_name,_age);

         select max(stuId) into _id fromstudent;

end;

 

call insert_Student('wfz',23,@id);

select @id;

 

mysql> createtrigger update_Student BEFORE update on student FOR EACH ROW

-> select * from student;

触发器不允许返回结果

 

create trigger update_Student BEFORE update on studentFOR EACH ROW 

insert into student value(null,'zxx',28);

mysql的触发器目前不能对当前表进行操作

 

create trigger update_Student BEFORE update on studentFOR EACH ROW 

delete from articles where id=8;

这个例子不是很好,最好是用删除一个用户时,顺带删除该用户的所有帖子

这里要注意使用OLD.id

 

触发器用处还是很多的,比如校内网、开心网、Facebook,你发一个日志,自动通知好友,其实就是在增加日志时做一个后触发,再向通知表中写入条目。因为触发器效率高。而UCH没有用触发器,效率和数据处理能力都很低。

存储过程的实验步骤:

mysql> delimiter |

mysql> create procedure insertArticle_Procedure(pTitle varchar(50),pBid int,out

 pId int)

    -> begin

    -> insertinto article1 value(null,pTitle,pBid);

    -> selectmax(id) into pId from article1;

    -> end;

    -> |

Query OK, 0 rows affected (0.05 sec)

 

mysql> call insertArticle_Procedure('传智播客',1,@pid);

    -> |

Query OK, 0 rows affected (0.00 sec)

 

mysql> delimiter ;

mysql> select @pid;

+------+

| @pid |

+------+

| 3    |

+------+

1 row in set (0.00 sec)

 

mysql> select * from article1;

+----+--------------+------+

| id | title       | bid  |

+----+--------------+------+

| 1  | test         | 1   |

| 2  | chuanzhiboke| 1    |

| 3  | 传智播客     |1    |

+----+--------------+------+

3 rows in set (0.00 sec)

 

触发器的实验步骤:

create table board1(id int primary keyauto_increment,name varchar(50),ar

ticleCount int);

 

create table article1(id int primary keyauto_increment,title varchar(50)

,bid int references board1(id));

 

delimiter |

 

create trigger insertArticle_Trigger after insert onarticle1 for each ro

w begin

    -> updateboard1 set articleCount=articleCount+1 where id= NEW.bid;

    -> end;

    -> |

 

delimiter ;

 

insert into board1 value (null,'test',0);

 

insert into article1 value(null,'test',1);

还有,每插入一个帖子,都希望将版面表中的最后发帖时间,帖子总数字段进行同步更新,用触发器做效率就很高。下次课设计这样一个案例,写触发器时,对于最后发帖时间可能需要用declare方式声明一个变量,或者是用NEW.posttime来生成。

 

4、数据库三范式是什么?

第一范式(1NF):字段具有原子性,不可再分。所有关系型数据库系统都满足第一范式)

         数据库表中的字段都是单一属性的,不可再分。例如,姓名字段,其中的姓和名必须作为一个整体,无法区分哪部分是姓,哪部分是名,如果要区分出姓和名,必须设计成两个独立的字段。

 

  第二范式(2NF):

第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。

要求数据库表中的每个实例或行必须可以被惟一地区分。通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键。

 

第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。

  

 第三范式的要求如下:

满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。

所以第三范式具有如下特征:
         1
,每一列只有一个值
         2
,每一行都能区分。
         3
,每一个表都不包含其他表已经包含的非主关键字信息。

例如,帖子表中只能出现发帖人的id,而不能出现发帖人的id,还同时出现发帖人姓名,否则,只要出现同一发帖人id的所有记录,它们中的姓名部分都必须严格保持一致,这就是数据冗余。

 

5、说出一些数据库优化方面的经验?

PreparedStatement一般来说比Statement性能高:一个sql 发给服务器去执行,涉及步骤:语法检查、语义分析,编译,缓存

“inert into uservalues(1,1,1)”-à二进制

“inert into uservalues(2,2,2)”-à二进制

“inert into uservalues(?,?,?)”-à二进制

 

 

 

有外键约束会影响插入和删除性能,如果程序能够保证数据的完整性,那在设计数据库时就去掉外键。(比喻:就好比免检产品,就是为了提高效率,充分相信产品的制造商)

(对于hibernate来说,就应该有一个变化:empleyee->Deptment对象,现在设计时就成了employeeàdeptid

 

mysql帮助文档子查询章节的最后部分,例如,根据扫描的原理,下面的子查询语句要比第二条关联查询的效率高:

1.  select e.name,e.salary wheree.managerid=(select id from employee where name='zxx');

 

2.   select e.name,e.salary,m.name,m.salary fromemployees e,employees m where

 e.managerid = m.id and m.name='zxx';

 

表中允许适当冗余,譬如,主题帖的回复数量和最后回复时间等

将姓名和密码单独从用户表中独立出来。这可以是非常好的一对一的案例哟!

 

sql语句全部大写,特别是列名和表名都大写。特别是sql命令的缓存功能,更加需要统一大小写,sql语句à发给oracle服务器à语法检查和编译成为内部指令à缓存和执行指令。根据缓存的特点,不要拼凑条件,而是用?PreparedStatment

 

还有索引对查询性能的改进也是值得关注的。

 

备注:下面是关于性能的讨论举例

 

4航班 3个城市

 

m*n

 

select * fromflight,city where flight.startcityid=city.cityid and city.name='beijing';

 

m + n

 

 

select * from flightwhere startcityid = (select cityid from city where cityname='beijing');

 

select flight.id,'beijing',flight.flightTime from flight where startcityid =(select cityid from city where cityname='beijing')

6unionunion all有什么不同?

假设我们有一个表Student,包括以下字段与数据:

drop table student;

create table student
(
id int primary key,
name nvarchar2(50) not null,
score number not null
);

insert into student values(1,'Aaron',78);
insert into student values(2,'Bill',76);
insert into student values(3,'Cindy',89);
insert into student values(4,'Damon',90);
insert into student values(5,'Ella',73);
insert into student values(6,'Frado',61);
insert into student values(7,'Gill',99);
insert into student values(8,'Hellen',56);
insert into student values(9,'Ivan',93);
insert into student values(10,'Jay',90);

commit;

UnionUnion All的区别。

select *
from student
where id < 4

union

select *
from student
where id > 2 and id < 6

结果将是

1   Aaron    78
2    Bill    76
3    Cindy    89
4    Damon    90
5    Ella    73

如果换成Union All连接两个结果集,则返回结果是:

1   Aaron    78
2    Bill    76
3    Cindy    89
3    Cindy    89
4    Damon    90
5    Ella    73

可以看到,UnionUnion All的区别之一在于对重复结果的处理。

 

UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
  这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
 而UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
 从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL

7.分页语句

取出sql表中第31到40的记录(以自动增长ID为主键)

sql server方案1

    select top 10 * from twhere id not in (select top 30 id from t order by id ) orde by id

sql server方案2

    select top 10 * from twhere id in (select top 40 id from t order by id) order by id desc

 

mysql方案:select * from t order by id limit 30,10

 

oracle方案:select * from (select rownum r,* from t wherer<=40) where r>30

 

--------------------待整理进去的内容-------------------------------------

pageSize=20;

pageNo = 5;

 

1.分页技术1(直接利用sql语句进行分页,效率最高和最推荐的)

 

mysql:sql ="select * from articles limit " + (pageNo-1)*pageSize + ","+ pageSize;

oracle: sql ="select * from " +

                                                                           "(selectrownum r,* from " +

                                                                                    "(select * fromarticles order by postime desc)" +

                                                                           "whererownum<= " + pageNo*pageSize +") tmp " +

                                                                 "wherer>" + (pageNo-1)*pageSize;

注释:第7行保证rownum的顺序是确定的,因为oracle的索引会造成rownum返回不同的值

简洋提示:没有order by时,rownum按顺序输出,一旦有了order byrownum不按顺序输出了,这说明rownum是排序前的编号。如果对order by从句中的字段建立了索引,那么,rownum也是按顺序输出的,因为这时候生成原始的查询结果集时会参照索引表的顺序来构建。

 

sqlserver:sql ="select top 10 * from id not id(select top " + (pageNo-1)*pageSize +"id from articles)"

 

DataSource ds = newInitialContext().lookup(jndiurl);

Connection cn =ds.getConnection();

//"select * fromuser where id=?"  --->binarydirective

PreparedStatementpstmt = cn.prepareSatement(sql);

ResultSet rs =pstmt.executeQuery()

while(rs.next())

{

         out.println(rs.getString(1));

}

 

2.不可滚动的游标

pageSize=20;

pageNo = 5;

cn = null

stmt = null;

rs = null;

try

{

sqlserver:sql ="select  * from articles";

 

DataSource ds = newInitialContext().lookup(jndiurl);

Connection cn =ds.getConnection();

//"select * fromuser where id=?"  --->binarydirective

PreparedStatement pstmt= cn.prepareSatement(sql);

ResultSet rs =pstmt.executeQuery()

for(intj=0;j<(pageNo-1)*pageSize;j++)

{

         rs.next();

}

 

int i=0;

 

while(rs.next()&& i<10)

{

         i++;

         out.println(rs.getString(1));

}

}

cacth(){}

finnaly

{

         if(rs!=null)try{rs.close();}catch(Exceptione){}

         if(stm.........

         if(cn............

}

 

3.可滚动的游标

pageSize=20;

pageNo = 5;

cn = null

stmt = null;

rs = null;

try

{

sqlserver:sql ="select  * from articles";

 

DataSource ds = newInitialContext().lookup(jndiurl);

Connection cn = ds.getConnection();

//"select * fromuser where id=?"  --->binarydirective

PreparedStatementpstmt = cn.prepareSatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,...);

//根据上面这行代码的异常SQLFeatureNotSupportedException,就可判断驱动是否支持可滚动游标

 

ResultSet rs =pstmt.executeQuery()

rs.absolute((pageNo-1)*pageSize)

int i=0;

while(rs.next()&& i<10)

{

         i++;

         out.println(rs.getString(1));

}

}

cacth(){}

finnaly

{

         if(rs!=null)try{rs.close();}catch(Exceptione){}

         if(stm.........

         if(cn............

}

8.用一条SQL语句查询出每门课都大于80分的学生姓名 

name   kecheng   fenshu
张三     语文      81
张三     数学      75
李四     语文      76
李四     数学      90
王五     语文      81
王五     数学      100
王五     英语      90

准备数据的sql代码:

create table score(id int primary key auto_increment,namevarchar(20),subject varchar(20),score int);

insert into score values

(null,'张三','语文',81),

(null,'张三','数学',75),

(null,'李四','语文',76),

(null,'李四','数学',90),

(null,'王五','语文',81),

(null,'王五','数学',100),

(null,'王五 ','英语',90);

 

提示:当百思不得其解时,请理想思维,把小变成大做,把大变成小做,

 

答案:
A: select distinct name from score  where  name not in (selectdistinct name from score where score<=80)

 

B:select distincename t1 from score where 80< all (select score from score where name=t1);

 

9.所有部门之间的比赛组合

一个叫department的表,里面只有一个字段name,一共有4条纪录,分别是a,b,c,d,对应四个球对,现在四个球对进行比赛,用一条sql语句显示所有可能的比赛组合.

答:select a.name, b.name
from team a, team b
where a.name < b.name

 

10.每个月份的发生额都比101科目多的科目

请用SQL语句实现:从TestDB数据表中查询出所有月份的发生额都比101科目相应月份的发生额高的科目。请注意:TestDB中有很多科目,都有112月份的发生额。
AccID
:科目代码,Occmonth:发生额月份,DebitOccur:发生额。
数据库名:JcyAudit,数据集:Select * from TestDB

准备数据的sql代码:

drop table if exists TestDB;

create table TestDB(id int primary key auto_increment,AccIDvarchar(20), Occmonth date, DebitOccur bigint);

insert into TestDB values

(null,'101','1988-1-1',100),

(null,'101','1988-2-1',110),

(null,'101','1988-3-1',120),

(null,'101','1988-4-1',100),

(null,'101','1988-5-1',100),

(null,'101','1988-6-1',100),

(null,'101','1988-7-1',100),

(null,'101','1988-8-1',100);

--复制上面的数据,故意把第一个月份的发生额数字改小一点

insert into TestDB values

(null,'102','1988-1-1',90),

(null,'102','1988-2-1',110),

(null,'102','1988-3-1',120),

(null,'102','1988-4-1',100),

(null,'102','1988-5-1',100),

(null,'102','1988-6-1',100),

(null,'102','1988-7-1',100),

(null,'102','1988-8-1',100);

--复制最上面的数据,故意把所有发生额数字改大一点

insert into TestDB values

(null,'103','1988-1-1',150),

(null,'103','1988-2-1',160),

(null,'103','1988-3-1',180),

(null,'103','1988-4-1',120),

(null,'103','1988-5-1',120),

(null,'103','1988-6-1',120),

(null,'103','1988-7-1',120),

(null,'103','1988-8-1',120);

--复制最上面的数据,故意把所有发生额数字改大一点

insert into TestDB values

(null,'104','1988-1-1',130),

(null,'104','1988-2-1',130),

(null,'104','1988-3-1',140),

(null,'104','1988-4-1',150),

(null,'104','1988-5-1',160),

(null,'104','1988-6-1',170),

(null,'104','1988-7-1',180),

(null,'104','1988-8-1',140);

--复制最上面的数据,故意把第二个月份的发生额数字改小一点

insert into TestDB values

(null,'105','1988-1-1',100),

(null,'105','1988-2-1',80),

(null,'105','1988-3-1',120),

(null,'105','1988-4-1',100),

(null,'105','1988-5-1',100),

(null,'105','1988-6-1',100),

(null,'105','1988-7-1',100),

(null,'105','1988-8-1',100);

答案:
select distinct AccID from TestDB

where AccID not in

         (selectTestDB.AccIDfrom TestDB,

                    (select * from TestDB where AccID='101') asdb101

         whereTestDB.Occmonth=db101.Occmonth and TestDB.DebitOccur<=db101.DebitOccur

         );

 

11.统计每年每月的信息

year  month amount
1991   1     1.1
1991   2     1.2
1991   3     1.3
1991   4     1.4
1992   1     2.1
1992   2     2.2
1992   3     2.3
1992   4     2.4
查成这样一个结果
year m1  m2  m3  m4
1991 1.1 1.2 1.3 1.4
1992 2.1 2.2 2.3 2.4

提示:这个与工资条非常类似,与学生的科目成绩也很相似。

 

准备sql语句:

drop table if exists sales;

create table sales(id intauto_increment primary key,year varchar(10), month varchar(10), amount float(2,1));

insert into sales values

(null,'1991','1',1.1),

(null,'1991','2',1.2),

(null,'1991','3',1.3),

(null,'1991','4',1.4),

(null,'1992','1',2.1),

(null,'1992','2',2.2),

(null,'1992','3',2.3),

(null,'1992','4',2.4);


答案一、
select sales.year ,

(select t.amount from sales t wheret.month='1' and t.year= sales.year) '1',

(select t.amount from sales t wheret.month='1' and t.year= sales.year) '2',

(select t.amount from sales t wheret.month='1' and t.year= sales.year) '3',

(select t.amount from sales t wheret.month='1' and t.year= sales.year) as '4'

from sales  group by year;

 

12.显示文章标题,发帖人、最后回复时间

表:id,title,postuser,postdate,parentid

准备sql语句:

drop table if exists articles;

create table articles(id int auto_increment primary key,titlevarchar(50), postuser varchar(10), postdate datetime,parentid int references articles(id));

insert into articles values

(null,'第一条','张三','1998-10-10 12:32:32',null),

(null,'第二条','张三','1998-10-10 12:34:32',null),

(null,'第一条回复1','李四','1998-10-10 12:35:32',1),

(null,'第二条回复1','李四','1998-10-10 12:36:32',2),

(null,'第一条回复2','王五','1998-10-10 12:37:32',1),

(null,'第一条回复3','李四','1998-10-10 12:38:32',1),

(null,'第二条回复2','李四','1998-10-10 12:39:32',2),

(null,'第一条回复4','王五','1998-10-10 12:39:40',1);

 

答案:

select a.title,a.postuser,

         (selectmax(postdate) from articles where parentid=a.id) reply

from articles a where a.parentid is null;

 

注释:子查询可以用在选择列中,也可用于where的比较条件中,还可以用于from从句中。

13.删除除了id号不同,其他都相同的学生冗余信息

2.学生表如下:
id
   学号   姓名课程编号课程名称分数
1        2005001 
张三  0001      数学    69
2        2005002 
李四  0001      数学    89
3        2005001 
张三  0001      数学    69
A: delete from tablename where id
not in(select min(id) from tablename group by 学号,姓名,课程编号,课程名称,分数)

实验:

create tablestudent2(id int auto_increment primary key,code varchar(20),name varchar(20));

insert into student2values(null,'2005001','张三'),(null,'2005002','李四'),(null,'2005001','张三');

 

//如下语句,mysql报告错误,可能删除依赖后面统计语句,而删除又导致统计语句结果不一致。

 

delete from student2where id not in(select min(id) from student2 group by name);

//但是,如下语句没有问题:

select *  from student2 where id not in(select min(id)from student2 group by name);

//于是,我想先把分组的结果做成虚表,然后从虚表中选出结果,最后再将结果作为删除的条件数据。

delete from student2where id not in(select mid from (select min(id) mid

from student2 groupby name) as t);

或者:

delete from student2where id not in(select min(id) from (select * from s

tudent2) as t groupby t.name);

14.航空网的几个航班查询题:

表结构如下:

flight{flightID,StartCityID,endCityID,StartTime}

city{cityID,CityName)

实验环境:

create tablecity(cityID int auto_increment primary key,cityName varchar(20));

create table flight(flightID int auto_increment primary key,

         StartCityID int referencescity(cityID),

         endCityID  int references city(cityID),

         StartTime timestamp);

//航班本来应该没有日期部分才好,但是下面的题目当中涉及到了日期

insert into cityvalues(null,'北京'),(null,'上海'),(null,'广州');

insert into flightvalues

         (null,1,2,'9:37:23'),(null,1,3,'9:37:23'),(null,1,2,'10:37:23'),(null,2,3,'10:37:23');

 

 

1、查询起飞城市是北京的所有航班,按到达城市的名字排序

 

 

参与运算的列是我起码能够显示出来的那些列,但最终我不一定把它们显示出来。各个表组合出来的中间结果字段中必须包含所有运算的字段。

 

  select * from flight f,city c

         where f.endcityid = c.cityid andstartcityid =

         (select c1.cityid from city c1 wherec1.cityname = "北京")

         order by c.cityname asc;

 

mysql> selectflight.flightid,'北京' startcity,e.cityname from flight,city e wh

ere flight.endcityid=e.cityidand flight.startcityid=(select cityid from city wh

ere cityname='北京');

 

mysql> selectflight.flightid,s.cityname,e.cityname from flight,city s,city e wh

ereflight.startcityid=s.cityid and s.cityname='北京' and flight.endCityId=e.cit

yID order bye.cityName desc;

 

 

2、查询北京到上海的所有航班纪录(起飞城市,到达城市,起飞时间,航班号)

selectc1.CityName,c2.CityName,f.StartTime,f.flightID

from city c1,cityc2,flight f

wheref.StartCityID=c1.cityID

andf.endCityID=c2.cityID

and c1.cityName='北京'

and c2.cityName='上海'

3、查询具体某一天(2005-5-8)的北京到上海的的航班次数

select count(*) from

(selectc1.CityName,c2.CityName,f.StartTime,f.flightID

from city c1,cityc2,flight f

wheref.StartCityID=c1.cityID

andf.endCityID=c2.cityID

and c1.cityName='北京'

and c2.cityName='上海'

and 查帮助获得的某个日期处理函数(startTime) like '2005-5-8%'

 

mysql中提取日期部分进行比较的示例代码如下:

select * from flightwhere date_format(starttime,'%Y-%m-%d')='1998-01-02'

15.查出比经理薪水还高的员工信息:

Drop table if notexists employees;

create tableemployees(id int primary key auto_increment,name varchar(50)

,salary int,managerid int references employees(id));

insert into employeesvalues (null,' lhm',10000,null), (null,' zxx',15000,1

),(null,'flx',9000,1),(null,'tg',10000,2),(null,'wzg',10000,3);

 

Wzg大于flx,lhm大于zxx

 

解题思路:

     根据sql语句的查询特点,是逐行进行运算,不可能两行同时参与运算。

涉及了员工薪水和经理薪水,所有,一行记录要同时包含两个薪水,所有想到要把这个表自关联组合一下。

     首先要组合出一个包含有各个员工及该员工的经理信息的长记录,譬如,左半部分是员工,右半部分是经理。而迪卡尔积会组合出很多垃圾信息,先去除这些垃圾信息。

 

select e.* fromemployees e,employees m where e.managerid=m.id and e.sala

ry>m.salary;

16、求出小于45岁的各个老师所带的大于12岁的学生人数

数据库中有3个表 teacher 表,student表,tea_stu关系表。
teacher
teaID name age
student
stuID name age
teacher_student
teaID stuID
要求用一条sql查询出这样的结果
1.
显示的字段要有老师name, age 每个老师所带的学生人数
2
只列出老师age40以下,学生age12以上的记录

预备知识:

      1.sql语句是对每一条记录依次处理,条件为真则执行动作(select,insert,delete,update

       2.只要是迪卡尔积,就会产生“垃圾”信息,所以,只要迪卡尔积了,我们首先就要想到清除“垃圾”信息

实验准备:

       drop table if existstea_stu;

       drop table if existsteacher;

       drop table if existsstudent;

      create tableteacher(teaID int primary key,name varchar(50),age int);

      create tablestudent(stuID int primary key,name varchar(50),age int);

      create tabletea_stu(teaID int references teacher(teaID),stuID int referencesstudent(stuID));

insertinto teacher values(1,'zxx',45), (2,'lhm',25) , (3,'wzg',26) , (4,'tg',27);

insertinto student values(1,'wy',11), (2,'dh',25) , (3,'ysq',26) , (4,'mxc',27);

insertinto tea_stu values(1,1), (1,2), (1,3);

insertinto tea_stu values(2,2), (2,3), (2,4);

 insert into tea_stu values(3,3), (3,4), (3,1);

insertinto tea_stu values(4,4), (4,1), (4,2) , (4,3);

 

结果:2à3,3à2,4à3

 

解题思路:(真实面试答题时,也要写出每个分析步骤,如果纸张不够,就找别人要)

1要会统计分组信息,统计信息放在中间表中:

selectteaid,count(*) from tea_stu group by teaid;

 

2接着其实应该是筛除掉小于12岁的学生,然后再进行统计,中间表必须与student关联才能得到12岁以下学生和把该学生记录从中间表中剔除,代码是:

selecttea_stu.teaid,count(*) total from student,tea_stu

wherestudent.stuid=tea_stu.stuid and student.age>12 group by tea_stu.teaid

 

3.接着把上面的结果做成虚表与teacher进行关联,并筛除大于45的老师

selectteacher.teaid,teacher.name,total from teacher ,(select tea_stu.tea

id,count(*)total from student,tea_stu where student.stuid=tea_stu.stuid and stu

dent.age>12group by tea_stu.teaid) as tea_stu2 whereteacher.teaid=tea_stu2.tea

id andteacher.age<45;

 

 

17.求出发帖最多的人:

selectauthorid,count(*) total from articles

group by authorid

having total=

(select max(total2) from (select count(*) total2 fromarticles group by authorid) as t);

 

selectt.authorid,max(t.total) from

select authorid,count(*) total from articles as t

这条语句不行,因为max只有一列,不能与其他列混淆。

 

selectauthorid,count(*) total from articles

group by authoridhaving total=max(total)也不行。

 

18、一个用户表中有一个积分字段,假如数据库中有100多万个用户,若要在每年第一天凌晨将积分清零,你将考虑什么,你将想什么办法解决?

alter table dropcolumn score;

alter table addcolunm score int;

可能会很快,但是需要试验,试验不能拿真实的环境来操刀,并且要注意,

这样的操作时无法回滚的,在我的印象中,只有inertupdate deleteDML语句才能回滚,

对于createtable,drop table ,alter tableDDL语句是不能回滚。

 

 

解决方案一,update userset score=0;

解决方案二,假设上面的代码要执行好长时间,超出我们的容忍范围,那我就alter table user drop column score;alter table user addcolumn score int

 

下面代码实现每年的那个凌晨时刻进行清零。

Runnable runnable =

         new Runnable(){

                   public void run(){

                            clearDb();

                            schedule(this,newDate(new Date().getYear()+1,0,0));

                            }                

                            };

 

schedule(runnable,

         new Date(new Date().getYear()+1,0,1));

 

19、一个用户具有多个角色,请查询出该表中具有该用户的所有角色的其他用户。

select count(*) asnum,tb.id

from

 tb,

 (select role from tb where id=xxx) as t1

where

 tb.role = t1.role and tb.id != t1.id

group by tb.id

having

         num = select count(role) from tb whereid=xxx;

20数据库触发器等使用

Oracle序列

Create Sequence

Increment by 1

Start with 1

Maxvalue 100

Minvalue 1

Cycle

Chche 4

使用nextval currval访问序列

Select Sequence.currvalfrom dual 查看序列,在使用的时候直接把Sequence.currval放入相应的字段即可

创建行级触发器

Create or replace sssbefore insert …… for each row

Begin

 select seq_userid.nextval into :new.stuid  from dual;

end;

commit; 在使用触发器的时候一般是位了做主键自增列,所以这个时候要用序列

创建存储过程

Creare or replaceprocedure pro_cc

Begin

Select * from …

end

执行存储过程:excute pro_cc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值